1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2015 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2016-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 /*
20  * f_readpcx.c
21  * Based on (public domain) code from Russell Marks
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"		/* restrict */
27 #endif
28 
29 #include "f_readpcx.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <X11/X.h>
35 
36 #include "resources.h"
37 #include "object.h"
38 #include "f_util.h"
39 #include "f_picobj.h"
40 #include "w_setup.h"
41 
42 /* This is based on: */
43 
44 /* pcx2ppm 1.2 - convert pcx to ppm
45  * based on zgv's readpcx.c
46  * public domain by RJM
47  *
48  * this is incredibly messy
49  *
50  * 1999-03-16	updated to support 24-bit.
51  */
52 
53 typedef unsigned char byte;
54 
55 struct pcxhed
56   {
57   byte manuf,ver,encod,bpp;		/* 0 - 3 gen format */
58   byte x1lo,x1hi,y1lo,y1hi;		/* 4 - 11 size */
59   byte x2lo,x2hi,y2lo,y2hi;
60   byte unused1[4];			/* 12 - 15 scrn size */
61   byte pal16[48];			/* 16 - 63 4-bit palette */
62   byte reserved;			/* 64 reserved */
63   byte nplanes;				/* 65 num of bitplanes */
64   byte bytelinelo,bytelinehi;		/* 66 - 67 bytes per line */
65   byte unused2[60];			/* 68 - 127 unused */
66   };  /* palette info is after image data */
67 
68 
69 /* prototypes */
70 static void	dispbyte(unsigned char *ptr,int *xp,int *yp,int c,int w,int h,
71 			int real_bpp,int byteline,int *planep,int *pmaskp);
72 
73 static int	_read_pcx(FILE *pcxfile, F_pic *pic);
74 
75 
76 int
read_pcx(F_pic * pic,struct xfig_stream * restrict pic_stream)77 read_pcx(F_pic *pic, struct xfig_stream *restrict pic_stream)
78 {
79 	/* make scale factor smaller for metric */
80 	const double scale =
81 		(appres.INCHES ? (double)PIX_PER_INCH : 2.54*PIX_PER_CM)
82 		/ DISPLAY_PIX_PER_INCH;
83 
84 	if (*pic_stream->name) {
85 		/* called directly from read_picobj() */
86 		if (!rewind_stream(pic_stream))
87 			return FileInvalid;
88 	}
89 	/* otherwise, called from read_gif(), rewind not necessary */
90 
91 	if (_read_pcx(pic_stream->fp, pic) != PicSuccess)
92 		return FileInvalid;
93 
94 	pic->pixmap = None;
95 	pic->hw_ratio = (float)pic->pic_cache->bit_size.y
96 					/ pic->pic_cache->bit_size.x;
97 	pic->pic_cache->subtype = T_PIC_PCX;
98 	pic->pic_cache->size_x = pic->pic_cache->bit_size.x * scale;
99 	pic->pic_cache->size_y = pic->pic_cache->bit_size.y * scale;
100 	/* if monochrome display map bitmap */
101 	if (tool_cells <= 2 || appres.monochrome)
102 		map_to_mono(pic);
103 
104 	return PicSuccess;
105 }
106 
107 /* _read_pcx() is called from read_pcx() and read_epsf().
108    The latter is because the output of ghostscript is to a PCX
109    file (actually a pipe).
110 */
111 
112 void pcx_decode();
113 
_read_pcx(FILE * pcxfile,F_pic * pic)114 int _read_pcx(FILE *pcxfile, F_pic *pic)
115 {
116 	int		 i,w,h,bytepp,x,y,yy,byteline,plane,pmask;
117 	unsigned char	*pal;
118 	struct pcxhed	 header;
119 	int		 count, cnt;
120 	byte		 inbyte;
121 	int		 real_bpp;  /* how many bits per pixel file really is */
122 
123 	pic->pic_cache->bitmap=NULL;
124 
125 	fread(&header,1,sizeof(struct pcxhed),pcxfile);
126 	if (header.manuf!=10 || header.encod!=1)
127 	    return FileInvalid;
128 
129 	/* header.bpp=1, header.nplanes=1 = 1-bit.
130 	 * header.bpp=1, header.nplanes=2 = 2-bit.   - added B.V.Smith 6/00
131 	 * header.bpp=1, header.nplanes=3 = 3-bit.   - added B.V.Smith 6/00
132 	 * header.bpp=1, header.nplanes=4 = 4-bit.
133 	 * header.bpp=8, header.nplanes=1 = 8-bit.
134 	 * header.bpp=8, header.nplanes=3 = 24-bit.
135 	 * anything else gives an `unsupported' error.
136 	 */
137 	real_bpp=0;
138 	bytepp = 1;
139 	switch(header.bpp) {
140 	  case 1:
141 	    switch(header.nplanes) {
142 	      case 1: real_bpp=1; break;
143 	      case 2: real_bpp=2; break;
144 	      case 3: real_bpp=3; break;
145 	      case 4: real_bpp=4; break;
146 	    }
147 	    break;
148 
149 	  case 8:
150 	    switch(header.nplanes) {
151 	      case 1: real_bpp=8; break;
152 	      case 3: real_bpp=24;
153 		      if (tool_vclass == TrueColor && image_bpp == 4 &&
154 				      !appres.monochrome)
155 			      bytepp = 4;
156 		      else
157 			      bytepp = 3;
158 		      break;
159 	    }
160 	    break;
161 	  }
162 
163 	if (!real_bpp)
164 	    return FileInvalid;
165 
166 	if ((pal=calloc(768,1))==NULL)
167 	    return FileInvalid;
168 
169 	w=(header.x2lo+256*header.x2hi)-(header.x1lo+256*header.x1hi)+1;
170 	h=(header.y2lo+256*header.y2hi)-(header.y1lo+256*header.y1hi)+1;
171 	byteline=header.bytelinelo+256*header.bytelinehi;
172 
173 	if (w==0 || h==0)
174 	    return FileInvalid;
175 
176 	x=0; y=0;
177 					/* why (h+2) ? */
178 	if ((pic->pic_cache->bitmap=malloc(w*(h+2)*bytepp))==NULL)
179 	    return FileInvalid;
180 
181 	/* need this if more than one bitplane, and also if one bitplane */
182 	memset(pic->pic_cache->bitmap, 0, w * h * bytepp);
183 
184 	/* start reading image */
185 	for (yy=0; yy<h; yy++) {
186 	  plane = 0;
187 	  pmask = 1;
188 
189 	  y = yy;
190 	  x = 0;
191 	  while (y == yy) {
192 	    inbyte=fgetc(pcxfile);
193 	    if ((inbyte & 0xC0) != 0xC0) {
194 	      dispbyte(pic->pic_cache->bitmap,&x,&y,inbyte,w,h,real_bpp,
195 		byteline,&plane,&pmask);
196 	    } else {
197 	      cnt = inbyte & 0x3F;
198 	      inbyte = fgetc(pcxfile);
199 	      for (count=0; count<cnt; count++)
200 		dispbyte(pic->pic_cache->bitmap,&x,&y,inbyte,w,h,real_bpp,
201 			byteline,&plane,&pmask);
202 	    }
203 	  }
204 	}
205 
206 	pic->pic_cache->bit_size.x = w;
207 	pic->pic_cache->bit_size.y = h;
208 
209 	/* read palette */
210 	switch(real_bpp) {
211 	    unsigned char	*src;
212 	    unsigned char	*dst;
213 
214 	    case 1:
215 	    case 2:
216 	    case 3:
217 	    case 4:
218 		/* 1 to 4 bit, palette is embedded in header */
219 		pic->pic_cache->numcols = 1 << real_bpp;
220 		for (x=0; x < pic->pic_cache->numcols; x++) {
221 		    pic->pic_cache->cmap[x].red   = header.pal16[x*3  ];
222 		    pic->pic_cache->cmap[x].green = header.pal16[x*3+1];
223 		    pic->pic_cache->cmap[x].blue  = header.pal16[x*3+2];
224 		}
225 		break;
226 
227 	    case 8:
228 		/* 8-bit */
229 		/*
230 		 * The palette at the end is separated by a byte, value 12, from
231 		 * the image data. For a regular file, one could also
232 		 * fseek(pcxfile, -768L, SEEK_END)
233 		 */
234 		i = fgetc(pcxfile);
235 		while (i != 12)
236 			i = fgetc(pcxfile);
237 		for (x=0; x<256; x++) {
238 		    pic->pic_cache->cmap[x].red   = fgetc(pcxfile);
239 		    pic->pic_cache->cmap[x].green = fgetc(pcxfile);
240 		    pic->pic_cache->cmap[x].blue  = fgetc(pcxfile);
241 		}
242 		/* start with 256 */
243 		pic->pic_cache->numcols = 256;
244 		/* ignore duplicate white entries after real colors */
245 		for (i = pic->pic_cache->numcols-1; i >=0 ; i--) {
246 		if (pic->pic_cache->cmap[i].red != 255 ||
247 		    pic->pic_cache->cmap[i].green != 255 ||
248 		    pic->pic_cache->cmap[i].blue != 255)
249 			break;
250 		}
251 		if (i < pic->pic_cache->numcols-2)
252 		    pic->pic_cache->numcols = i+2;
253 		break;
254 
255 	    case 24:
256 		/* no palette ...*/
257 		if (tool_vclass == TrueColor && image_bpp == 4 &&
258 				!appres.monochrome) {
259 			/* ...expand BGR to BGRA */
260 			src = pic->pic_cache->bitmap + w * h * 3;
261 			dst = pic->pic_cache->bitmap + w * h * 4 - 1;
262 			while (src > pic->pic_cache->bitmap + 2) {
263 				*(--dst) = *(--src);
264 				*(--dst) = *(--src);
265 				*(--dst) = *(--src);
266 				--dst;
267 			}
268 			pic->pic_cache->numcols = -1;
269 		} else {
270 			/* ...or use neural net to reduce to 256 colors
271 			   with palette */
272 			if (!map_to_palette(pic))
273 				return FileInvalid;
274 		}
275 		break;
276 	}
277 	return PicSuccess;
278 }
279 
280 
281 void
dispbyte(unsigned char * ptr,int * xp,int * yp,int c,int w,int h,int real_bpp,int byteline,int * planep,int * pmaskp)282 dispbyte(unsigned char *ptr,int *xp,int *yp,int c,int w,int h,
283               int real_bpp,int byteline,int *planep,int *pmaskp)
284 {
285 	int f;
286 	unsigned char *dstptr;
287 
288 	switch(real_bpp) {
289 	  case 1:
290 	  case 2:
291 	  case 3:
292 	  case 4:
293 		/* mono or 4-bit */
294 
295 		if ((*yp)>=h)
296 		    return;
297 
298 		dstptr=ptr+(*yp)*w+*xp;
299 		w=byteline*8;
300 
301 		for (f=0; f<8; f++) {
302 		   *dstptr++|=(c&(0x80>>(f&7)))?(*pmaskp):0;
303 		   (*xp)++;
304 		   if (*xp>=w) {
305 			if (real_bpp==1) {
306 			    (*xp)=0,(*yp)++;
307 			    return;
308 			}
309 			(*xp)=0;
310 			(*planep)++;
311 			(*pmaskp)<<=1;
312 			if (real_bpp==2) {
313 			    if (*planep==2) {
314 				(*yp)++;
315 				return;
316 			    }
317 			} else if (real_bpp==3) {
318 			    if (*planep==3) {
319 				(*yp)++;
320 				return;
321 			    }
322 			} else {
323 			    /* otherwise, it's 4 bpp */
324 			    if (*planep==4) {
325 				(*yp)++;
326 				return;
327 			    }
328 			}
329 		    }
330 		    if ((*yp)>=h)
331 			return;
332 		}
333 		break;
334 
335 	  case 8:
336 		*(ptr+(*yp)*w+*xp)=c;
337 		(*xp)++;
338 		if (*xp>=byteline) {
339 		    (*xp)=0;
340 		    (*yp)++;
341 		}
342 		break;
343 
344 	  case 24:
345 		*(ptr+((*yp)*w+*xp)*3+(2-(*planep)))=c;
346 		(*xp)++;
347 		if (*xp>=byteline) {
348 		    (*xp)=0;
349 		    (*planep)++; /* no need to change pmask */
350 		    if (*planep==3) {
351 			(*yp)++;
352 			return;
353 		    }
354 		}
355 		break;
356 	  }
357 }
358