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