1 /*
2  * xvpcx.c - load routine for PCX format pictures
3  *
4  * LoadPCX(fname, pinfo)  -  loads a PCX file
5  */
6 
7 #include "copyright.h"
8 
9 /*
10  * the following code has been derived from code written by
11  *  Eckhard Rueggeberg  (Eckhard.Rueggeberg@ts.go.dlr.de)
12  */
13 
14 
15 #include "xv.h"
16 
17 /* offsets into PCX header */
18 #define PCX_ID      0
19 #define PCX_VER     1
20 #define PCX_ENC     2
21 #define PCX_BPP     3
22 #define PCX_XMINL   4
23 #define PCX_XMINH   5
24 #define PCX_YMINL   6
25 #define PCX_YMINH   7
26 #define PCX_XMAXL   8
27 #define PCX_XMAXH   9
28 #define PCX_YMAXL   10
29 #define PCX_YMAXH   11
30                           /* hres (12,13) and vres (14,15) not used */
31 #define PCX_CMAP    16    /* start of 16*3 colormap data */
32 #define PCX_PLANES  65
33 #define PCX_BPRL    66
34 #define PCX_BPRH    67
35 
36 #define PCX_MAPSTART 0x0c	/* Start of appended colormap	*/
37 
38 
39 static int  pcxLoadImage8  PARM((const char *, FILE *, PICINFO *, byte *));
40 static int  pcxLoadImage24 PARM((const char *, FILE *, PICINFO *, byte *));
41 static void pcxLoadRaster  PARM((FILE *, byte *, int, byte *, int, int));
42 static int  pcxError       PARM((const char *, const char *));
43 
44 
45 
46 /*******************************************/
LoadPCX(fname,pinfo)47 int LoadPCX(fname, pinfo)
48      char    *fname;
49      PICINFO *pinfo;
50 /*******************************************/
51 {
52   FILE  *fp;
53   long   filesize;
54   byte   hdr[128];
55   int    i, colors, gray, fullcolor;
56   const char  *bname;
57 
58   pinfo->type = PIC8;
59   pinfo->pic     = (byte *) NULL;
60   pinfo->comment = (char *) NULL;
61 
62   bname = BaseName(fname);
63 
64   /* open the stream */
65   fp = xv_fopen(fname,"r");
66   if (!fp) return (pcxError(bname, "unable to open file"));
67 
68 
69   /* figure out the file size */
70   fseek(fp, 0L, 2);
71   filesize = ftell(fp);
72   fseek(fp, 0L, 0);
73 
74 
75   /* read the PCX header */
76   fread(hdr, (size_t) 128, (size_t) 1, fp);
77   if (ferror(fp) || feof(fp)) {
78     fclose(fp);
79     return pcxError(bname, "EOF reached in PCX header.\n");
80   }
81 
82   if (hdr[PCX_ID] != 0x0a || hdr[PCX_VER] > 5) {
83     fclose(fp);
84     return pcxError(bname,"unrecognized magic number");
85   }
86 
87   pinfo->w = (hdr[PCX_XMAXL] + ((int) hdr[PCX_XMAXH]<<8))
88            - (hdr[PCX_XMINL] + ((int) hdr[PCX_XMINH]<<8));
89 
90   pinfo->h = (hdr[PCX_YMAXL] + ((int) hdr[PCX_YMAXH]<<8))
91            - (hdr[PCX_YMINL] + ((int) hdr[PCX_YMINH]<<8));
92 
93   pinfo->w++;  pinfo->h++;
94 
95   colors = 1 << (hdr[PCX_BPP] * hdr[PCX_PLANES]);
96   fullcolor = (hdr[PCX_BPP] == 8 && hdr[PCX_PLANES] == 3);
97 
98   if (DEBUG) {
99     fprintf(stderr,"PCX: %dx%d image, version=%d, encoding=%d\n",
100 	    pinfo->w, pinfo->h, hdr[PCX_VER], hdr[PCX_ENC]);
101     fprintf(stderr,"   BitsPerPixel=%d, planes=%d, BytePerRow=%d, colors=%d\n",
102 	    hdr[PCX_BPP], hdr[PCX_PLANES],
103 	    hdr[PCX_BPRL] + ((int) hdr[PCX_BPRH]<<8),
104 	    colors);
105   }
106 
107   if (colors>256 && !fullcolor) {
108     fclose(fp);
109     return pcxError(bname,"No more than 256 colors allowed in PCX file.");
110   }
111 
112   if (hdr[PCX_ENC] != 1) {
113     fclose(fp);
114     return pcxError(bname,"Unsupported PCX encoding format.");
115   }
116 
117   /* load the image, the image function fills in pinfo->pic */
118   if (!fullcolor) {
119     if (!pcxLoadImage8(bname, fp, pinfo, hdr)) {
120       fclose(fp);
121       return 0;
122     }
123   }
124   else {
125     if (!pcxLoadImage24(bname, fp, pinfo, hdr)) {
126       fclose(fp);
127       return 0;
128     }
129   }
130 
131 
132   if (ferror(fp) | feof(fp))    /* just a warning */
133     pcxError(bname, "PCX file appears to be truncated.");
134 
135   if (colors>16 && !fullcolor) {       /* handle trailing colormap */
136     while (1) {
137       i=getc(fp);
138       if (i==PCX_MAPSTART || i==EOF) break;
139     }
140 
141     for (i=0; i<colors; i++) {
142       pinfo->r[i] = getc(fp);
143       pinfo->g[i] = getc(fp);
144       pinfo->b[i] = getc(fp);
145     }
146 
147     if (ferror(fp) || feof(fp)) {
148       pcxError(bname,"Error reading PCX colormap.  Using grayscale.");
149       for (i=0; i<256; i++) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;
150     }
151   }
152   else if (colors<=16) {   /* internal colormap */
153     for (i=0; i<colors; i++) {
154       pinfo->r[i] = hdr[PCX_CMAP + i*3];
155       pinfo->g[i] = hdr[PCX_CMAP + i*3 + 1];
156       pinfo->b[i] = hdr[PCX_CMAP + i*3 + 2];
157     }
158   }
159 
160   if (colors == 2) {    /* b&w */
161     if (MONO(pinfo->r[0], pinfo->g[0], pinfo->b[0]) ==
162 	MONO(pinfo->r[1], pinfo->g[1], pinfo->b[1])) {
163       /* create cmap */
164       pinfo->r[0] = pinfo->g[0] = pinfo->b[0] = 255;
165       pinfo->r[1] = pinfo->g[1] = pinfo->b[1] = 0;
166       if (DEBUG) fprintf(stderr,"PCX: no cmap:  using 0=white,1=black\n");
167     }
168   }
169 
170 
171   fclose(fp);
172 
173 
174 
175   /* finally, convert into XV internal format */
176 
177 
178   pinfo->type    = fullcolor ? PIC24 : PIC8;
179   pinfo->frmType = -1;    /* no default format to save in */
180 
181   /* check for grayscaleitude */
182   gray = 0;
183   if (!fullcolor) {
184     for (i=0; i<colors; i++) {
185       if ((pinfo->r[i] != pinfo->g[i]) || (pinfo->r[i] != pinfo->b[i])) break;
186     }
187     gray = (i==colors) ? 1 : 0;
188   }
189 
190 
191   if (colors > 2 || (colors==2 && !gray)) {  /* grayscale or PseudoColor */
192     pinfo->colType = (gray) ? F_GREYSCALE : F_FULLCOLOR;
193     sprintf(pinfo->fullInfo,
194 	    "%s PCX, %d plane%s, %d bit%s per pixel.  (%ld bytes)",
195 	    (gray) ? "Greyscale" : "Color",
196 	    hdr[PCX_PLANES], (hdr[PCX_PLANES]==1) ? "" : "s",
197 	    hdr[PCX_BPP],    (hdr[PCX_BPP]==1) ? "" : "s",
198 	    filesize);
199   }
200   else {
201     pinfo->colType = F_BWDITHER;
202     sprintf(pinfo->fullInfo, "B&W PCX.  (%ld bytes)", filesize);
203   }
204 
205   sprintf(pinfo->shrtInfo, "%dx%d PCX.", pinfo->w, pinfo->h);
206   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
207 
208   return 1;
209 }
210 
211 
212 
213 /*****************************/
pcxLoadImage8(fname,fp,pinfo,hdr)214 static int pcxLoadImage8(fname, fp, pinfo, hdr)
215      const char *fname;
216      FILE    *fp;
217      PICINFO *pinfo;
218      byte    *hdr;
219 {
220   /* load an image with at most 8 bits per pixel */
221 
222   byte *image;
223   int count;
224 
225   /* note:  overallocation to make life easier... */
226   count = (pinfo->h + 1) * pinfo->w + 16;  /* up to 65537*65536+16 (~ 65552) */
227   if (pinfo->w <= 0 || pinfo->h <= 0 || count/pinfo->w < pinfo->h) {
228     pcxError(fname, "Bogus 8-bit PCX file!!");
229     return (0);
230   }
231   image = (byte *) malloc((size_t) count);
232   if (!image) FatalError("Can't alloc 'image' in pcxLoadImage8()");
233 
234   xvbzero((char *) image, (size_t) count);
235 
236   switch (hdr[PCX_BPP]) {
237   case 1:   pcxLoadRaster(fp, image, 1, hdr, pinfo->w, pinfo->h);   break;
238   case 8:   pcxLoadRaster(fp, image, 8, hdr, pinfo->w, pinfo->h);   break;
239   default:
240     pcxError(fname, "Unsupported # of bits per plane.");
241     free(image);
242     return (0);
243   }
244 
245   pinfo->pic = image;
246   return 1;
247 }
248 
249 
250 /*****************************/
pcxLoadImage24(fname,fp,pinfo,hdr)251 static int pcxLoadImage24(fname, fp, pinfo, hdr)
252      const char *fname;
253      FILE *fp;
254      PICINFO *pinfo;
255      byte *hdr;
256 {
257   byte *pix, *pic24, scale[256];
258   int   c, i, j, w, h, maxv, cnt, planes, bperlin, nbytes, count;
259 
260   w = pinfo->w;  h = pinfo->h;
261 
262   planes = (int) hdr[PCX_PLANES];  /* 255 max, but can't get here unless = 3 */
263   bperlin = hdr[PCX_BPRL] + ((int) hdr[PCX_BPRH]<<8);  /* 65535 max */
264 
265   j = h*planes;          /* w and h are limited to 65536, planes to 3 */
266   count = w*j;           /* ...so this could wrap up to 3 times */
267   nbytes = bperlin*j;    /* ...and this almost 3 times */
268   if (w <= 0 || h <= 0 || planes <= 0 || bperlin <= 0 ||
269       j/h < planes || count/w < j || nbytes/bperlin < j) {
270     pcxError(fname, "Bogus 24-bit PCX file!!");
271     return (0);
272   }
273 
274   /* allocate 24-bit image */
275   pic24 = (byte *) malloc((size_t) count);
276   if (!pic24) FatalError("Can't malloc 'pic24' in pcxLoadImage24()");
277 
278   xvbzero((char *) pic24, (size_t) count);
279 
280   maxv = 0;
281   pix = pinfo->pic = pic24;
282   i = 0;      /* planes, in this while loop */
283   j = 0;      /* bytes per line, in this while loop */
284 
285   while (nbytes > 0 && (c = getc(fp)) != EOF) {
286     if ((c & 0xC0) == 0xC0) {   /* have a rep. count */
287       cnt = c & 0x3F;
288       c = getc(fp);
289       if (c == EOF) { getc(fp); break; }
290     }
291     else cnt = 1;
292 
293     if (c > maxv)  maxv = c;
294 
295     while (cnt-- > 0) {
296       if (j < w) {
297 	*pix = c;
298 	pix += planes;
299       }
300       j++;
301       nbytes--;
302       if (j == bperlin) {
303 	j = 0;
304 	if (++i < planes) {
305 	  pix -= (w*planes)-1;  /* next plane on this line */
306 	}
307 	else {
308 	  pix -= (planes-1);    /* start of next line, first plane */
309 	  i = 0;
310 	}
311       }
312     }
313   }
314 
315 
316   /* scale all RGB to range 0-255, if they aren't */
317 
318   if (maxv<255) {
319     for (i=0; i<=maxv; i++) scale[i] = (i * 255) / maxv;
320 
321     for (i=0, pix=pic24; i<h; i++) {
322       if ((i&0x3f)==0) WaitCursor();
323       for (j=0; j<w*planes; j++, pix++) *pix = scale[*pix];
324     }
325   }
326 
327   return 1;
328 }
329 
330 
331 
332 /*****************************/
pcxLoadRaster(fp,image,depth,hdr,w,h)333 static void pcxLoadRaster(fp, image, depth, hdr, w,h)
334      FILE    *fp;
335      byte    *image, *hdr;
336      int      depth,w,h;
337 {
338   /* supported:  8 bits per pixel, 1 plane; or 1 bit per pixel, 1-8 planes */
339 
340   int row, bcnt, bperlin, pad;
341   int i, j, b, cnt, mask, plane, pmask;
342   byte *oldimage;
343 
344   bperlin = hdr[PCX_BPRL] + ((int) hdr[PCX_BPRH]<<8);  /* 65535 max */
345   if (depth == 1) pad = (bperlin * 8) - w;
346              else pad = bperlin - w;
347 
348   row = bcnt = 0;
349 
350   plane = 0;  pmask = 1;  oldimage = image;
351 
352   while ( (b=getc(fp)) != EOF) {
353     if ((b & 0xC0) == 0xC0) {   /* have a rep. count */
354       cnt = b & 0x3F;
355       b = getc(fp);
356       if (b == EOF) { getc(fp); return; }
357     }
358     else cnt = 1;
359 
360     for (i=0; i<cnt; i++) {
361       if (depth == 1) {
362 	for (j=0, mask=0x80; j<8; j++) {
363 	  *image++ |= ((b & mask) ? pmask : 0);
364 	  mask = mask >> 1;
365 	}
366       }
367       else *image++ = (byte) b;
368 
369       bcnt++;
370 
371       if (bcnt == bperlin) {     /* end of a line reached */
372 	bcnt = 0;
373 	plane++;
374 
375 	if (plane >= (int) hdr[PCX_PLANES]) {   /* moved to next row */
376 	  plane = 0;
377 	  image -= pad;
378 	  oldimage = image;
379 	  row++;
380 	  if (row >= h) return;   /* done */
381 	}
382 	else {   /* next plane, same row */
383 	  image = oldimage;
384 	}
385 
386 	pmask = 1 << plane;
387       }
388     }
389   }
390 }
391 
392 
393 
394 /*******************************************/
pcxError(fname,st)395 static int pcxError(fname,st)
396      const char *fname, *st;
397 {
398   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
399   return 0;
400 }
401 
402