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