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