1 /*
2  * in_pcx.cpp: loads PCX images
3  * modified by pts@fazekas.hu at Fri Apr 12 22:16:08 CEST 2002
4  * -- Fri Apr 12 23:54:57 CEST 2002
5  *
6  * xvpcx.c - load routine for PCX format pictures
7  *
8  * LoadPCX(fname, pinfo)  -  loads a PCX file
9  */
10 
11 /**** pts ****/
12 #include "config2.h"
13 #include "image.hpp"
14 
15 #if USE_IN_PCX
16 #include "error.hpp"
17 #include "gensio.hpp"
18 #include <string.h>
19 
20 
21 /* Imp: palette handling etc. according to PCX_VER, see decode.c */
22 #define dimen Image::Sampled::dimen_t
23 #define pcxError(bname,conststr) Error::sev(Error::WARNING) << "PCX: " conststr << (Error*)0
24 #define WaitCursor()
25 #define xvbzero(p,len) memset(p, '\0', len)
26 #define FatalError(conststr) Error::sev(Error::EERROR) << "PCX: " conststr << (Error*)0
27 #define return_pcxError(bname, conststr) Error::sev(Error::EERROR) << "PCX: " conststr << (Error*)0
28 #define byte unsigned char
29 #define PCX_SIZE_T slen_t
30 #define malloc_byte(n) new byte[n]
31 #define PCX_FREE(p) delete [] (p)
32 /* the following list give indicies into saveColors[] array in xvdir.c */
33 #define F_FULLCOLOR 0
34 #define F_GREYSCALE 1
35 #define F_BWDITHER  2
36 /* values 'picType' can take */
37 #define PIC8  8
38 #define PIC24 24
39 #define xv_fopen(filename,read_mode) fopen(filename,"rb")
40 #define BaseName(x) ((char*)0)
41 #define PARM(parm) parm
42 /* info structure filled in by the LoadXXX() image reading routines */
43 #ifndef USE_PCX_DEBUG_MESSAGES
44 #define USE_PCX_DEBUG_MESSAGES 0
45 #endif
46 
47 typedef struct { byte *pic;                  /* image data */
48 		 dimen w, h;                 /* pic size */
49 #if 0 /**** pts ****/
50 		 byte  r[256],g[256],b[256];
51 #else
52                  /* byte pal[3*256]; */
53                  byte *pal;
54 #  define PAL_R(pinfo,idx) (pinfo)->pal[3*(idx)]
55 #  define PAL_G(pinfo,idx) (pinfo)->pal[3*(idx)+1]
56 #  define PAL_B(pinfo,idx) (pinfo)->pal[3*(idx)+2]
57 #endif		                             /* colormap, if PIC8 */
58 #if 0 /**** pts ****/
59 		 int   colType;              /* def. Color type to save in */
60 		 int   type;                 /* PIC8 or PIC24 */
61 		 int   normw, normh;         /* 'normal size' of image file
62 					        (normally eq. w,h, except when
63 						doing 'quick' load for icons */
64 		 int   frmType;              /* def. Format type to save in */
65 		 char  fullInfo[128];        /* Format: field in info box */
66 		 char  shrtInfo[128];        /* short format info */
67 		 char *comment;              /* comment text */
68 		 int   numpages;             /* # of page files, if >1 */
69 		 char  pagebname[64];        /* basename of page files */
70 #endif
71 	       } PICINFO;
72 
73 
74 /* #include "copyright.h" */
75 
76 /*
77  * the following code has been derived from code written by
78  *  Eckhard Rueggeberg  (Eckhard.Rueggeberg@ts.go.dlr.de)
79  */
80 
81 
82 /* #include "xv.h" */
83 
84 /* offsets into PCX header */
85 #define PCX_ID      0
86 #define PCX_VER     1
87 #define PCX_ENC     2
88 #define PCX_BPP     3
89 #define PCX_XMINL   4
90 #define PCX_XMINH   5
91 #define PCX_YMINL   6
92 #define PCX_YMINH   7
93 #define PCX_XMAXL   8
94 #define PCX_XMAXH   9
95 #define PCX_YMAXL   10
96 #define PCX_YMAXH   11
97                           /* hres (12,13) and vres (14,15) not used */
98 #define PCX_CMAP    16    /* start of 16*3 colormap data */
99 #define PCX_PLANES  65
100 #define PCX_BPRL    66
101 #define PCX_BPRH    67
102 
103 #define PCX_MAPSTART 0x0c	/* Start of appended colormap	*/
104 
105 
106 static int  pcxLoadImage8  PARM((char *, FILE *, PICINFO *, byte *));
107 static int  pcxLoadImage24 PARM((char *, FILE *, PICINFO *, byte *));
108 static void pcxLoadRaster  PARM((FILE *, byte *, int, byte *, dimen, dimen));
109 #if 0 /**** pts ****/
110 static int  pcxError       PARM((char *, char *));
111 #endif
112 
multiply_check(PCX_SIZE_T a,PCX_SIZE_T b)113 static PCX_SIZE_T multiply_check(PCX_SIZE_T a, PCX_SIZE_T b) {
114   const PCX_SIZE_T result = a * b;
115   /* Check for overflow. Works only if everything is unsigned. */
116   if (result / a != b) FatalError("Image too large.");
117   return result;
118 }
119 
multiply_check(PCX_SIZE_T a,PCX_SIZE_T b,PCX_SIZE_T c)120 static PCX_SIZE_T multiply_check(PCX_SIZE_T a, PCX_SIZE_T b, PCX_SIZE_T c) {
121   return multiply_check(multiply_check(a, b), c);
122 }
123 
124 /*******************************************/
125 static Image::Sampled *LoadPCX
126 #if 0 /**** pts ****/
127   ___((char *fname, PICINFO *pinfo), (fname, pinfo), (char    *fname; PICINFO *pinfo;))
128 #else
129   ___((FILE *fp, PICINFO *pinfo), (fname, pinfo), (char    *fname; PICINFO *pinfo;))
130 #endif
131 /*******************************************/
132 {
133   Image::Sampled *ret=(Image::Sampled*)NULLP;
134   byte   hdr[128];
135 #if 0 /**** pts ****/
136   long   filesize;
137   char  *bname;
138   FILE  *fp;
139   char *errstr; byte *image;
140   int gray;
141 #endif
142   int    i, colors, fullcolor;
143 
144   pinfo->pic     = (byte *) NULL;
145   pinfo->pal     = (byte *) NULL;
146 #if 0 /**** pts ****/
147   pinfo->type = PIC8;
148   pinfo->comment = (char *) NULL;
149   bname = BaseName(fname);
150 
151   /* open the stream */
152   fp = xv_fopen(fname,"r");
153   if (!fp) return_pcxError(bname, "unable to open file");
154 #endif
155 
156 #if 0 /**** pts ****/
157   /* figure out the file size */
158   fseek(fp, 0L, 2);
159   filesize = ftell(fp);
160   fseek(fp, 0L, 0);
161 #endif
162 
163   /* read the PCX header */
164   if (fread(hdr, (PCX_SIZE_T) 128, (PCX_SIZE_T) 1, fp) != 1 ||
165       ferror(fp) || feof(fp)) {
166     /* fclose(fp); */
167     return_pcxError(bname, "EOF reached in PCX header.\n");
168   }
169 
170   if (hdr[PCX_ID] != 0x0a || hdr[PCX_VER] > 5) {
171     /* fclose(fp); */
172     return_pcxError(bname,"unrecognized magic number");
173   }
174 
175   pinfo->w = (hdr[PCX_XMAXL] + ((dimen) hdr[PCX_XMAXH]<<8))
176            - (hdr[PCX_XMINL] + ((dimen) hdr[PCX_XMINH]<<8));
177 
178   pinfo->h = (hdr[PCX_YMAXL] + ((dimen) hdr[PCX_YMAXH]<<8))
179            - (hdr[PCX_YMINL] + ((dimen) hdr[PCX_YMINH]<<8));
180 
181   pinfo->w++;  pinfo->h++;
182 
183   colors = 1 << (hdr[PCX_BPP] * hdr[PCX_PLANES]);
184   fullcolor = (hdr[PCX_BPP] == 8 && hdr[PCX_PLANES] == 3);
185 
186 #if USE_PCX_DEBUG_MESSAGES
187   fprintf(stderr,"PCX: %dx%d image, version=%d, encoding=%d\n",
188           pinfo->w, pinfo->h, hdr[PCX_VER], hdr[PCX_ENC]);
189   fprintf(stderr,"   BitsPerPixel=%d, planes=%d, BytePerRow=%d, colors=%d\n",
190           hdr[PCX_BPP], hdr[PCX_PLANES],
191           hdr[PCX_BPRL] + ((dimen) hdr[PCX_BPRH]<<8),
192           colors);
193 #endif
194 
195   if (colors>256 && !fullcolor) {
196     /* fclose(fp); */
197     return_pcxError(bname,"No more than 256 colors allowed in PCX file.");
198   }
199 
200   if (hdr[PCX_ENC] != 1) {
201     /* fclose(fp); */
202     return_pcxError(bname,"Unsupported PCX encoding format.");
203   }
204 
205   /* load the image, the image function fills in pinfo->pic */
206   if (!fullcolor) {
207     Image::Indexed *img=new Image::Indexed(pinfo->w, pinfo->h, colors, 8);
208     pinfo->pal=(byte*)img->getHeadp();
209     ASSERT_SIDE(pcxLoadImage8((char*)NULLP/*bname*/, fp, pinfo, hdr));
210     memcpy(img->getRowbeg(), pinfo->pic, multiply_check(pinfo->w, pinfo->h));
211     ret=img;
212   } else {
213     Image::RGB *img=new Image::RGB(pinfo->w, pinfo->h, 8);
214     ASSERT_SIDE(pcxLoadImage24((char*)NULLP/*bname*/, fp, pinfo, hdr));
215     memcpy(img->getRowbeg(), pinfo->pic, multiply_check(pinfo->w, pinfo->h, 3));
216     ret=img;
217   }
218   PCX_FREE(pinfo->pic);
219   pinfo->pic=(byte*)NULLP;
220 
221 
222   if (ferror(fp) | feof(fp))    /* just a warning */
223     pcxError(bname, "PCX file appears to be truncated.");
224 
225   if (colors>16 && !fullcolor) {       /* handle trailing colormap */
226     while (1) {
227       i=MACRO_GETC(fp);
228       if (i==PCX_MAPSTART || i==EOF) break;
229     }
230 
231 #if 0 /**** pts ****/
232     for (i=0; i<colors; i++) {
233       PAL_R(pinfo,i) = MACRO_GETC(fp);
234       PAL_G(pinfo,i) = MACRO_GETC(fp);
235       PAL_B(pinfo,i) = MACRO_GETC(fp);
236     }
237 #endif
238     if (fread(pinfo->pal, 1, colors*3, fp) != colors * 3 + 0U ||
239         ferror(fp) || feof(fp)) {
240       pcxError(bname,"Error reading PCX colormap.  Using grayscale.");
241       for (i=0; i<256; i++) PAL_R(pinfo,i) = PAL_G(pinfo,i) = PAL_B(pinfo,i) = i;
242     }
243   }
244   else if (colors<=16) {   /* internal colormap */
245 #if 0 /**** pts ****/
246     for (i=0; i<colors; i++) {
247       PAL_R(pinfo,i) = hdr[PCX_CMAP + i*3];
248       PAL_G(pinfo,i) = hdr[PCX_CMAP + i*3 + 1];
249       PAL_B(pinfo,i) = hdr[PCX_CMAP + i*3 + 2];
250     }
251 #else
252     memcpy(pinfo->pal, hdr+PCX_CMAP, colors*3);
253 #endif
254   }
255 
256   if (colors == 2) {    /* b&w */
257 #if 0 /**** pts ****/
258     if (MONO(PAL_R(pinfo,0), PAL_G(pinfo,0), PAL_B(pinfo,0)) ==
259 	MONO(PAL_R(pinfo,1), PAL_G(pinfo,1), PAL_B(pinfo,1))) {
260 #else
261     if (PAL_R(pinfo,0)==PAL_R(pinfo,1) && PAL_G(pinfo,0)==PAL_G(pinfo,1) && PAL_B(pinfo,0)==PAL_B(pinfo,1)) {
262 #endif
263       /* create cmap */
264       PAL_R(pinfo,0) = PAL_G(pinfo,0) = PAL_B(pinfo,0) = 255;
265       PAL_R(pinfo,1) = PAL_G(pinfo,1) = PAL_B(pinfo,1) = 0;
266 #if USE_PCX_DEBUG_MESSAGES
267       fprintf(stderr,"PCX: no cmap:  using 0=white,1=black\n");
268 #endif
269     }
270   }
271   /* fclose(fp); */
272 
273   /* finally, convert into XV internal format */
274 #if 0 /**** pts ****/
275   pinfo->type    = fullcolor ? PIC24 : PIC8;
276   pinfo->frmType = -1;    /* no default format to save in */
277 #endif
278 
279 #if 0 /**** pts ****/
280   /* check for grayscaleitude */
281   gray = 0;
282   if (!fullcolor) {
283     for (i=0; i<colors; i++) {
284       if ((PAL_R(pinfo,i) != PAL_G(pinfo,i)) || (PAL_R(pinfo,i) != PAL_B(pinfo,i))) break;
285     }
286     gray = (i==colors) ? 1 : 0;
287   }
288 
289 
290   if (colors > 2 || (colors==2 && !gray)) {  /* grayscale or PseudoColor */
291     pinfo->colType = (gray) ? F_GREYSCALE : F_FULLCOLOR;
292 #if 0 /**** pts ****/
293     sprintf(pinfo->fullInfo,
294 	    "%s PCX, %d plane%s, %d bit%s per pixel.  (%ld bytes)",
295 	    (gray) ? "Greyscale" : "Color",
296 	    hdr[PCX_PLANES], (hdr[PCX_PLANES]==1) ? "" : "s",
297 	    hdr[PCX_BPP],    (hdr[PCX_BPP]==1) ? "" : "s",
298 	    filesize);
299 #endif
300   }
301   else {
302     pinfo->colType = F_BWDITHER;
303 #if 0 /**** pts ****/
304     sprintf(pinfo->fullInfo, "B&W PCX.  (%ld bytes)", filesize);
305 #endif
306   }
307 
308 #if 0 /**** pts ****/
309   sprintf(pinfo->shrtInfo, "%dx%d PCX.", pinfo->w, pinfo->h);
310   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
311 #endif
312 #endif
313 
314   return ret;
315 }
316 
317 /*****************************/
318 static int pcxLoadImage8 ___((char *fname, FILE *fp, PICINFO *pinfo, byte *hdr), (fname, fp, pinfo, hdr),
319     (char    *fname;
320      FILE    *fp;
321      PICINFO *pinfo;
322      byte    *hdr;))
323 {
324   /* load an image with at most 8 bits per pixel */
325   (void)fname; /**** pts ****/
326 
327   byte *image;
328 
329   image = (byte *) malloc_byte(multiply_check(pinfo->h, pinfo->w));
330   if (!image) FatalError("Can't alloc 'image' in pcxLoadImage8()");
331 
332   xvbzero((char *) image, multiply_check(pinfo->h, pinfo->w));
333 
334   switch (hdr[PCX_BPP]) {
335   case 1: case 2: case 4: case 8: pcxLoadRaster(fp, image, hdr[PCX_BPP], hdr, pinfo->w, pinfo->h);   break;
336   default:
337     PCX_FREE(image);
338     return_pcxError(fname, "Unsupported # of bits per plane.");
339   }
340 
341   pinfo->pic = image;
342   return 1;
343 }
344 
345 
346 /*****************************/
347 static int pcxLoadImage24 ___((char *fname, FILE *fp, PICINFO *pinfo, byte *hdr), (fname, fp, pinfo, hdr),
348     (char *fname;
349      FILE *fp;
350      PICINFO *pinfo;
351      byte *hdr;))
352 {
353   byte *pix, *pic24;
354   int   c;
355   unsigned i, j, w, h, cnt, planes, bperlin, nbytes;
356 #if 0 /***** pts ****/
357   int maxv; /* ImageMagick does not have one */
358   byte scale[256];
359 #endif
360 
361   (void)fname; /**** pts ****/
362 
363   w = pinfo->w;  h = pinfo->h;
364 
365   planes = (unsigned) hdr[PCX_PLANES];
366   bperlin = hdr[PCX_BPRL] + ((dimen) hdr[PCX_BPRH]<<8);
367 
368   /* allocate 24-bit image */
369   const PCX_SIZE_T alloced = multiply_check(w, h, planes);
370   const PCX_SIZE_T w_planes = multiply_check(w, planes);
371   pic24 = (byte *) malloc_byte(alloced);
372   if (!pic24) FatalError("couldn't malloc 'pic24'");
373 
374   /* This may still fail with a segfault for large values of alloced, even
375    * if malloc_byte has succeeded.
376    */
377   xvbzero((char *) pic24, alloced);
378   fprintf(stderr, "AAA3\n");
379 
380 #if 0 /**** pts ****/
381   maxv = 0;
382 #endif
383   pix = pinfo->pic = pic24;
384   i = 0;      /* planes, in this while loop */
385   j = 0;      /* bytes per line, in this while loop */
386   nbytes = multiply_check(bperlin, h, planes);
387 
388   while (nbytes > 0 && (c = MACRO_GETC(fp)) != EOF) {
389     if (c>=0xC0) {   /* have a rep. count */
390       cnt = c & 0x3F;
391       c = MACRO_GETC(fp);
392       if (c == EOF) { MACRO_GETC(fp); break; }
393     }
394     else cnt = 1;
395     if (cnt > nbytes) FatalError("Repeat count too large.");
396 
397 #if 0 /**** pts ****/
398     if (c > maxv)  maxv = c;
399 #endif
400 
401     while (cnt-- > 0) {
402       if (j < w) {
403 	*pix = c;
404 	pix += planes;
405       }
406       j++;
407       nbytes--;
408       if (j == bperlin) {
409 	j = 0;
410 	if (++i < planes) {
411 	  pix -= w_planes-1;  /* next plane on this line */
412 	}
413 	else {
414 	  pix -= planes-1;    /* start of next line, first plane */
415 	  i = 0;
416 	}
417       }
418     }
419   }
420   if (nbytes != 0) pcxError(0, "Image data truncated.");
421 
422 
423 #if 0 /**** pts ****/
424   /* scale all RGB to range 0-255, if they aren't */
425 
426   if (maxv<255) {
427     for (i=0; i<=maxv; i++) scale[i] = (i * 255) / maxv;
428 
429     for (i=0, pix=pic24; i<h; i++) {
430       if ((i&0x3f)==0) WaitCursor();
431       for (j=0; j<w_planes; j++, pix++) *pix = scale[*pix];
432     }
433   }
434 #endif
435 
436   return 1;
437 }
438 
439 
440 
441 /*****************************/
442 static void pcxLoadRaster ___((FILE *fp, byte *image, int depth, byte *hdr, dimen w, dimen h), (fp, image, depth, hdr, w, h),
443     (FILE    *fp;
444      byte    *image, *hdr;
445      int      depth;
446      dimen w,h;))
447 {
448   /* was supported:  8 bits per pixel, 1 plane, or 1 bit per pixel, 1-8 planes */
449 
450   unsigned row, bcnt, bperlin, pad, cnt, pmask, i, pleft;
451   int b;
452   byte *oldimage;
453 
454   bperlin = hdr[PCX_BPRL] + ((dimen) hdr[PCX_BPRH]<<8);
455   pad = (depth == 1) ? bperlin * 8 : bperlin;
456   if (pad < w) FatalError("pad too small");
457   pad -= w;
458 
459   row = bcnt = 0;
460 
461   pmask = 1;  oldimage = image;
462   pleft=hdr[PCX_PLANES];
463 
464   while ( (b=MACRO_GETC(fp)) != EOF) {
465     if (b>=0xC0) {   /* have a rep. count */
466       cnt = b & 0x3F;
467       b = MACRO_GETC(fp);
468       if (b == EOF) { MACRO_GETC(fp); return; }
469     }
470     else cnt = 1;
471 
472     for (i=0; i<cnt; i++) {
473       switch (depth) {
474        case 1:
475         *image++|=(b&0x80)?pmask:0;
476         *image++|=(b&0x40)?pmask:0;
477         *image++|=(b&0x20)?pmask:0;
478         *image++|=(b&0x10)?pmask:0;
479         *image++|=(b&0x8)?pmask:0;
480         *image++|=(b&0x4)?pmask:0;
481         *image++|=(b&0x2)?pmask:0;
482         *image++|=(b&0x1)?pmask:0;
483         break;
484        case 2: /**** pts ****/
485         *image++|=((b>>6)&3)*pmask;
486         *image++|=((b>>4)&3)*pmask;
487         *image++|=((b>>2)&3)*pmask;
488         *image++|=((b   )&3)*pmask;
489         break;
490        case 4: /**** pts ****/
491         *image++|=((b>>4)&15)*pmask;
492         *image++|=((b   )&15)*pmask;
493         break;
494        default:
495         *image++=(byte)b;
496       }
497 
498       bcnt++;
499 
500       if (bcnt == bperlin) {     /* end of a line reached */
501 	bcnt = 0;
502 
503 	if (--pleft==0) {   /* moved to next row */
504           pleft=hdr[PCX_PLANES];
505 	  pmask=1;
506 	  image -= pad;
507 	  oldimage = image;
508 	  row++;
509 	  if (row >= h) return;   /* done */
510 	}
511 	else {   /* next plane, same row */
512 	  image = oldimage;
513 	  pmask<<=depth;
514 	}
515       }
516     }
517   }
518 }
519 
520 #if 0 /**** pts ****/
521 /*******************************************/
522 static int pcxError(fname,st)
523      char *fname, *st;
524 {
525   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
526   return 0;
527 }
528 #endif
529 
in_pcx_reader(Image::Loader::UFD * ufd,SimBuffer::Flat const &)530 static Image::Sampled *in_pcx_reader(Image::Loader::UFD *ufd, SimBuffer::Flat const&) {
531   PICINFO pinfo_;
532   return LoadPCX(((Filter::UngetFILED*)ufd)->getFILE(/*seekable:*/false), &pinfo_);
533 }
in_pcx_checker(char buf[Image::Loader::MAGIC_LEN],char[Image::Loader::MAGIC_LEN],SimBuffer::Flat const &,Image::Loader::UFD *)534 static Image::Loader::reader_t in_pcx_checker(char buf[Image::Loader::MAGIC_LEN], char [Image::Loader::MAGIC_LEN], SimBuffer::Flat const&, Image::Loader::UFD*) {
535   return buf[PCX_ID]==0x0a
536       && (unsigned char)buf[PCX_VER]<=5
537       && buf[PCX_ENC]==1
538       && buf[PCX_BPP]<=8
539        ? in_pcx_reader : 0;
540 }
541 
542 #else
543 #define in_pcx_checker (Image::Loader::checker_t)NULLP
544 #endif /* USE_IN_PCX */
545 
546 Image::Loader in_pcx_loader = { "PCX", in_pcx_checker, 0 };
547 
548 /* __END__ */
549