1 /*
2 ** pcx.c - load a ZSoft PC Paintbrush (PCX) file for use inside xloadimage
3 **
4 **	Tim Northrup <tim@BRS.Com>
5 **	Adapted from code by Jef Poskanzer (see Copyright below).
6 **
7 **	Version 0.1 --  4/25/91 -- Initial cut
8 **
9 **  Copyright (c) 1991 Tim Northrup
10 **	(see file "tgncpyrght.h" for complete copyright information)
11 */
12 /*
13 ** Copyright (C) 1988 by Jef Poskanzer.
14 **
15 ** Permission to use, copy, modify, and distribute this software and its
16 ** documentation for any purpose and without fee is hereby granted, provided
17 ** that the above copyright notice appear in all copies and that both that
18 ** copyright notice and this permission notice appear in supporting
19 ** documentation.  This software is provided "as is" without express or
20 ** implied warranty.
21 **
22 ** This program (pcxtopbm) is based on the pcx2rf program by:
23 **   Mike Macgirvin
24 **   Stanford Relativity Gyro Program GP-B
25 **   Stanford, Calif. 94503
26 **   ARPA: mike@relgyro.stanford.edu
27 */
28 
29 #include <stdio.h>
30 #include "image.h"
31 #include "tgncpyrght.h"
32 #include "pcx.h"
33 
34 /*
35 **  pcxIdent
36 **
37 **	Identify passed file as a PC Paintbrush image or not
38 **
39 **	Returns 1 if file is a PCX file, 0 otherwise
40 */
41 
42 
pcxIdent(fullname,name)43 unsigned int pcxIdent ( fullname, name )
44 	char *fullname, *name;
45 {
46 	ZFILE *zf;
47 	unsigned int ret;
48 	int xmin;
49 	int xmax;
50 	int ymin;
51 	int ymax;
52 	int colors;
53 	char *type;
54 
55 	ret = 0;
56 	if (! (zf = zopen ( fullname )))
57 	    return ( 0 );
58 	PCXH = (PCXHeader *) lmalloc ( PCXHsize );
59 	if (zread ( zf, (byte *)PCXH, PCXHsize ) == PCXHsize) {
60 	    if ((PCXH->Zid == PCX_MAGIC) && (PCXH->Zver <= 5)) {
61 		xmin = Word ( PCXH->Zxminlo, PCXH->Zxminhi);
62 		xmax = Word ( PCXH->Zxmaxlo, PCXH->Zxmaxhi);
63 		ymin = Word ( PCXH->Zyminlo, PCXH->Zyminhi);
64 		ymax = Word ( PCXH->Zymaxlo, PCXH->Zymaxhi);
65 		xmax = xmax - xmin + 1;
66 		ymax = ymax - ymin + 1;
67 		colors = 1 << (PCXH->Zbpp * PCXH->Znplanes);
68 		type = " PC Paintbrush image\n";
69 		if (colors == 2)
70 		     printf ( "%s is a %dx%d monochrome%s",
71 				name, xmax, ymax, type );
72 		else printf ( "%s is a %dx%d %d color%s",
73 		   	 name, xmax, ymax, colors, type );
74 		ret = 1;
75 	    }
76 	}
77 	zclose ( zf );
78 	lfree ( (byte *)PCXH );
79 	return ( ret );
80 }
81 
82 
83 /*
84 **  PCX_Load_Raster
85 **
86 **	Load raster image data into passed image structure. Raster Data
87 **
88 **	means : Every bit is a pixel (if depth is 1) or every byte is a
89 **
90 **	pixel (if depth is 8).
91 **
92 **	Returns no value (void function)
93 */
94 
PCX_Load_Raster(zf,image,depth)95 static void PCX_Load_Raster ( zf, image, depth )
96 	ZFILE *zf;
97 	Image *image;
98 	int depth;
99 	/* Assertion : depth is 1 or 8 */
100 {
101 	byte *ptr	= &(image->data[0]);
102 	int row		= 0;
103 	int by_this_row = 0;
104 	int by_per_row;
105 	int linelen;
106 	int corrector;
107 	int i, b, cnt;
108 
109 	by_per_row   = Word ( PCXH->Zbprlo, PCXH->Zbprhi);
110 	if (depth == 1)
111 	    linelen  = (image->width / 8) + (image->width % 8 ? 1 : 0);
112 	else linelen = image->width;
113 	corrector    = by_per_row - linelen;
114 	/* bytes per row is always even, which means that there is an	*/
115 	/* excess byte if an odd nr of bytes would be sufficient.	*/
116 	/* As newBitImage allocated memory without this	excess, we have */
117 	/* to read one less. But as there are two cases of reading,	*/
118 	/* inside and outside of a run, I correct it afterwards, when	*/
119 	/* the line is complete.					*/
120 	while ((b = zgetc(zf)) != EOF) { 	/* Are we done ?	*/
121 	    if ((b & 0xC0) == 0xC0) {		/* have a rep. count	*/
122 		cnt = b & 0x3F;			/* mask rep. bits out	*/
123 		b = zgetc ( zf );		/* get real bits	*/
124 		if (b == EOF) {			/* Shouldn't happen !	*/
125 		    printf ( "Unexpected EOF\n" );
126 		    return;
127 		}
128 	    }
129 	    else cnt = 1;			/* no repeating this one*/
130 	    if (depth ==1) b = 255 - b;		/* Have to invert	*/
131 	    for ( i = 0; i < cnt; i++ ) {	/* store count times	*/
132 		*ptr++ = (byte) b;
133 		if (++by_this_row == by_per_row) {
134 		    row++;			/* start of a new line	*/
135 		    by_this_row = 0;		/* reset counter	*/
136 		    if (corrector) ptr--;	/*evtlly correct pointer*/
137 		    if ( row >= image->height ) {
138 #if 0
139 			/* happens a lot on valid images - jimf 10.28.91 */
140 			if (depth == 1)		/* Color : Map follows	*/
141 			    printf ("Warning: excess data ignored\n");
142 #endif
143 			return;
144 		    }
145 		}
146 	    }
147 	}
148 }
149 
150 /*
151 **  PCX_Planes
152 **
153 **	Load plane image data into passed image structure. Plane data
154 **
155 **	means : There are N planes, each containing M bits of a pixel
156 **
157 **	byte, where N * M is not greater than 8 and M divides 8. (The
158 **
159 **	cases M = 1 or 8, N = 1 are covered by PCX_Load_Raster).
160 **
161 **	Returns no value (void function)
162 */
163 
164 
PCX_Planes(zf,image,bpp,nr_pl)165 static void PCX_Planes ( zf, image, bpp, nr_pl )
166 	ZFILE *zf;
167 	Image *image;
168 	int bpp, nr_pl;
169 	/* Assertion : 							*/
170 	/* bpp is 1, 2 or 4 only, dividing 8 without remainder anyways,	*/
171 	/* bpp * nr_pl <= 8						*/
172 {
173 	byte *ptr, *sptr;
174 	register byte *tptr;
175 	int row		= 0;
176 	int by_this_row = 0;
177 	int by_per_row;
178 	int this_plane	= 0;
179 	int shifter	= 0;
180 	register int j;
181 	int i, b, cnt;
182 
183 	ptr = &(image->data[0]);
184 	by_per_row  = Word ( PCXH->Zbprlo, PCXH->Zbprhi);
185 	sptr = tptr = (byte *) lcalloc ( by_per_row*8 );
186 	/* We can't correct as easy as above, because we handle several	*/
187 	/* bit planes simultaneously, and we must not load beyond row	*/
188 	/* limits. So we load into a temporary row and copy it into	*/
189 	/* image structure when the row is completed.			*/
190 	while ((b = zgetc(zf)) != EOF) {
191 	    if ((b & 0xC0) == 0xC0) {	/* Get count and data as above	*/
192 		cnt = b & 0x3F;
193 		b = zgetc ( zf );
194 		if (b == EOF) {
195 		    printf ( "Unexpected EOF\n" );
196 		    return;
197 		}
198 	    }
199 	    else cnt = 1;
200 	    for ( i = 0; i < cnt; i++ ) {	/* Load data into temp.	*/
201 		switch (bpp) {
202 		    case 1 :
203 			*tptr++ |= (byte) (((b & 0x80 ) >> 7) << shifter);
204 			*tptr++ |= (byte) (((b & 0x40 ) >> 6) << shifter);
205 			*tptr++ |= (byte) (((b & 0x20 ) >> 5) << shifter);
206 			*tptr++ |= (byte) (((b & 0x10 ) >> 4) << shifter);
207 			*tptr++ |= (byte) (((b & 0x08 ) >> 3) << shifter);
208 			*tptr++ |= (byte) (((b & 0x04 ) >> 2) << shifter);
209 			*tptr++ |= (byte) (((b & 0x02 ) >> 1) << shifter);
210 			*tptr++ |= (byte)  ((b & 0x01 )       << shifter);
211 			/* This is not a loop for performance reasons.	*/
212 			/* Can't write e.g. ">> 2 - shifter", because 	*/
213 			/* that expression would get negative.		*/
214 			break;
215 		    case 2 :
216 			*tptr++ |= (byte) (((b & 0xC0 ) >> 6) << shifter);
217 			*tptr++ |= (byte) (((b & 0x30 ) >> 4) << shifter);
218 			*tptr++ |= (byte) (((b & 0x0C ) >> 2) << shifter);
219 			*tptr++ |= (byte)  ((b & 0x03 )       << shifter);
220 			break;
221 		    case 4 :
222 			*tptr++ |= (byte) (((b & 0xF0) >> 4) << shifter);
223 			*tptr++ |= (byte)  ((b & 0x0F)       << shifter);
224 			break;
225 		    default :; /* Can't happen if assertion holds	*/
226 		}
227 		if (++by_this_row == by_per_row) {	/* Row done ?	*/
228 		    by_this_row = 0;		 /* reset counter	*/
229 		    if (++this_plane == nr_pl) { /* was it last plane ?	*/
230 			row++;			 /* inc counter		*/
231 			tptr = sptr;		 /* get saved ptr	*/
232 			this_plane = shifter = 0;/* reset plane Nr	*/
233 			for (j = 0; j < image->width; j++) {
234 			    *ptr++  = *tptr;	/* store final data	*/
235 			    *tptr++ = 0;	/* clear temp data	*/
236 			}
237 			if ( row >= image->height )
238 			    return;		/* Done with image ?	*/
239 		    }
240 		    else			/* Prepare next plane	*/
241 			shifter = this_plane * bpp;
242 		    tptr = sptr;		/* Get saved ptr	*/
243 		}
244 	    }
245 	}
246 }
247 
248 
249 /*
250 **  PCX_LoadImage
251 **
252 **	Load PC Paintbrush file into the passed Image structure.
253 **
254 **	Returns no value (void function)
255 */
256 
257 
PCX_LoadImage(zf,image)258 static void PCX_LoadImage ( zf ,image )
259 	ZFILE *zf;
260 	Image *image;
261 {
262 	switch (PCXH->Zbpp) {	/* What kind of plane do we have ?	*/
263 	    case 1 :			/* Bit planes			*/
264 		if (PCXH->Znplanes == 1)	/* Only one : Read it	*/
265 		    PCX_Load_Raster ( zf, image, 1 );
266 		else PCX_Planes ( zf, image, 1, PCXH->Znplanes );
267 		break;
268 	    case 2 :			/* Two or four bits per plane	*/
269 	    case 4 :			/*      are read plane by plane */
270 		PCX_Planes ( zf, image, PCXH->Zbpp, PCXH->Znplanes );
271 		break;
272 	    case 8 :			/* Byte planes			*/
273 		if (PCXH->Znplanes == 1)	/* Only one : Read it	*/
274 		    PCX_Load_Raster ( zf, image, 8 );
275 		else {				/* More not allowed	*/
276 		    printf ("Only 1 plane allowed if 8 bits per plane\n");
277 		    exit (1);
278 		}
279 		break;
280 	    default :				/* Neither case found	*/
281 		printf ("%d bits per plane not supported\n", PCXH->Zbpp );
282 		exit (1);
283 	}
284 }
285 
286 
287 /*
288 **  pcxLoad
289 **
290 **	Load PCX Paintbrush file into an Image structure.
291 **
292 **	Returns pointer to allocated struct if successful, NULL otherwise
293 */
294 
pcxLoad(fullname,name,verbose)295 Image *pcxLoad ( fullname, name, verbose )
296 	char *fullname, *name;
297 	unsigned int verbose;
298 {
299 	ZFILE *zf;
300 	unsigned int i;
301 	int xmin;
302 	int xmax;
303 	int ymin;
304 	int ymax;
305 	int colors;
306 	PCXcolor *cmap;
307 	int clen;
308 	Image *image;
309 
310 	if ( ! (zf = zopen ( fullname )))	/* Open input file	*/
311 	    return ( (Image *) NULL);
312 	PCXH = (PCXHeader *) lmalloc ( PCXHsize );
313 	if (zread ( zf, (byte *)PCXH, PCXHsize ) != PCXHsize) {	/* Read header	*/
314 	    zclose ( zf );
315 	    return ( (Image *) NULL );
316 	}
317 	if ((PCXH->Zid != PCX_MAGIC) || (PCXH->Zver > 5)) {
318 	    zclose ( zf );		/* Is it PCX, Version less 5 ?	*/
319 	    return ( (Image *) NULL );
320 	}
321 	znocache(zf);			/* don't need caching anymore	*/
322 	xmin = Word ( PCXH->Zxminlo, PCXH->Zxminhi); /* Calculate sizes	*/
323 	xmax = Word ( PCXH->Zxmaxlo, PCXH->Zxmaxhi);
324 	ymin = Word ( PCXH->Zyminlo, PCXH->Zyminhi);
325 	ymax = Word ( PCXH->Zymaxlo, PCXH->Zymaxhi);
326 	xmax = xmax - xmin + 1;
327 	ymax = ymax - ymin + 1;
328 	colors = 1 << (PCXH->Zbpp * PCXH->Znplanes); /* Calculate colors*/
329 	if (verbose) {				/* Print Information	*/
330 	     if (colors == 2)
331 		printf ( "%s is a %dx%d monochrome PC Paintbrush image\n",
332 				name, xmax, ymax );
333 	     else printf ( "%s is a %dx%d %d color PC Paintbrush image\n",
334 				name, xmax, ymax, colors );
335 	}
336 	if (colors > 256) {
337 	    printf ( "No more than 256 colors allowed in PCX format\n" );
338 	    exit (1);
339 	}
340 	if (PCXH->Zenc == 0) {
341 	    printf ( "Unencoded PCX format not yet supported. Please" );
342 	    printf ( " email the uuencoded image\n to erueg@cfgauss." );
343 	    printf ( "uni-math.gwdg.de\n" );
344 	    exit (1);
345 	}
346 	if (colors == 2)	/* Allocate appropriate pbm array	*/
347 	    image = newBitImage ( xmax, ymax );
348 	else {
349 	    image = newRGBImage ( xmax, ymax, 8 );
350 	}
351 	PCX_LoadImage ( zf, image );
352 	if (colors > 16) {		/* Handle external colormap	*/
353 	    while ((i = zgetc(zf)) != PCX_MAPSTART);
354 	    clen = colors * 3;
355 	    cmap = (PCXcolor *) lmalloc ( clen );
356 	    if (zread ( zf, (byte *)cmap, clen ) != clen) {
357 		printf ( "EOF while reading colormap" );
358 		exit (1);
359 	    }
360 	    for ( i = 0; i < colors; i++) {
361 		*(image->rgb.red   + i) = (cmap [i].Zred   << 8);
362 		*(image->rgb.green + i) = (cmap [i].Zgreen << 8);
363 		*(image->rgb.blue  + i) = (cmap [i].Zblue  << 8);
364 	    }
365 	    image->rgb.used = colors;
366 	    lfree ( (byte *)cmap );
367 	}
368 	else if (colors > 2) {		/* Handle internal colormap	*/
369 	    for ( i = 0; i < colors; i++) {
370 		*(image->rgb.red   + i) = (PCXH->Zcmap [i].Zred   << 8);
371 		*(image->rgb.green + i) = (PCXH->Zcmap [i].Zgreen << 8);
372 		*(image->rgb.blue  + i) = (PCXH->Zcmap [i].Zblue  << 8);
373 	    }
374 	    image->rgb.used = colors;
375 	}
376 	zclose ( zf );
377 	lfree ( (byte *)PCXH );
378 	image->title = dupString(name);
379 	return ( image );
380 }
381