1 /*
2  * xvpm.c - load routine for 'pm' format pictures
3  *
4  * LoadPM();
5  * WritePM(fp, pic, ptype, w, h, r,g,b, numcols, style, comment)
6  */
7 
8 #include "copyright.h"
9 
10 #include "xv.h"
11 
12 
13 /**** PM.H ******/
14 #define	PM_MAGICNO	0x56494557		/* Hex for VIEW */
15 #define	PM_A		0x8000
16 #define	PM_C		0x8001
17 #define	PM_S		0x8002
18 #define	PM_I		0x8004
19 #define PM_F		0xc004
20 
21 #define PM_IOHDR_SIZE	(sizeof(pmpic)-(2*sizeof(char*)))
22 
23 typedef struct {
24 	int	pm_id;		/* Magic number for pm format files.	*/
25 	int	pm_np;		/* Number of planes. Normally 1.	*/
26 	int	pm_nrow;	/* Number of rows. 1 - MAXNELM.		*/
27 	int	pm_ncol;	/* Number of columns. 1 - MAXNELM.	*/
28 	int	pm_nband;	/* Number of bands.			*/
29 	int	pm_form;	/* Pixel format.			*/
30 	int	pm_cmtsize;	/* Number comment bytes. Includes NULL. */
31 	char	*pm_image;	/* The image itself.			*/
32 	char	*pm_cmt;	/* Transforms performed.		*/
33 } pmpic;
34 
35 
36 #define pm_nelm(p)	((p)->pm_ncol * (p)->pm_nrow)
37 #define pm_nbelm(p)	(pm_nelm(p) * (p)->pm_nband)
38 #define pm_psize(p)	(pm_nbelm(p) * (((p)->pm_form)&0xff))
39 #define pm_isize(p)	((p)->pm_np * pm_psize(p))
40 #define pm_npix(p)      (pm_nbelm(p) * (p)->pm_np)
41 
42 /***** end PM.H *****/
43 
44 
45 static pmpic thePic;
46 
47 static int  pmError  PARM((const char *, const char *));
48 static int  flip4    PARM((int));
49 static int  getint32 PARM((FILE *));
50 static void putint32 PARM((int, FILE *));
51 
52 
53 /*******************************************/
LoadPM(fname,pinfo)54 int LoadPM(fname, pinfo)
55      char    *fname;
56      PICINFO *pinfo;
57 /*******************************************/
58 {
59   /* returns '1' on success */
60 
61   FILE  *fp;
62   byte  *pic8;
63   int    isize,i,flipit,w,h,npixels,nRGBbytes;
64   const char  *bname;
65 
66   bname = BaseName(fname);
67   thePic.pm_image = (char *) NULL;
68   thePic.pm_cmt   = (char *) NULL;
69 
70   pinfo->pic = (byte *) NULL;
71   pinfo->comment = (char *) NULL;
72 
73 
74   fp = xv_fopen(fname,"r");
75   if (!fp) return( pmError(bname, "unable to open file") );
76 
77   /* read in the pmpic struct, one byte at a time */
78   thePic.pm_id      = getint32(fp);
79   thePic.pm_np      = getint32(fp);
80   thePic.pm_nrow    = getint32(fp);
81   thePic.pm_ncol    = getint32(fp);
82   thePic.pm_nband   = getint32(fp);
83   thePic.pm_form    = getint32(fp);
84   thePic.pm_cmtsize = getint32(fp);
85 
86   if (ferror(fp) || feof(fp)) return(pmError(bname, "error reading header"));
87 
88   flipit = 0;
89 
90   if (thePic.pm_id != PM_MAGICNO) {
91     thePic.pm_id = flip4(thePic.pm_id);
92     if (thePic.pm_id == PM_MAGICNO) flipit = 1;
93     else thePic.pm_id = flip4(thePic.pm_id);
94   }
95   if (thePic.pm_id != PM_MAGICNO) return( pmError(bname, "not a PM file") );
96 
97   if (flipit) {
98     thePic.pm_np      = flip4(thePic.pm_np);
99     thePic.pm_nrow    = flip4(thePic.pm_nrow);
100     thePic.pm_ncol    = flip4(thePic.pm_ncol);
101     thePic.pm_nband   = flip4(thePic.pm_nband);
102     thePic.pm_form    = flip4(thePic.pm_form);
103     thePic.pm_cmtsize = flip4(thePic.pm_cmtsize);
104     }
105 
106   w = thePic.pm_ncol;
107   h = thePic.pm_nrow;
108 
109   /* make sure that the input picture can be dealt with */
110   if ( thePic.pm_nband!=1 ||
111       (thePic.pm_form!=PM_I && thePic.pm_form!=PM_C) ||
112       (thePic.pm_form==PM_I && thePic.pm_np>1) ||
113       (thePic.pm_form==PM_C && (thePic.pm_np==2 || thePic.pm_np>4)) ) {
114     fprintf(stderr,"PM picture not in a displayable format.\n");
115     fprintf(stderr,"(ie, 1-plane PM_I, or 1-, 3-, or 4-plane PM_C)\n");
116 
117     return pmError(bname, "PM file in unsupported format");
118   }
119 
120 
121   isize = pm_isize(&thePic);
122   npixels = w*h;
123   nRGBbytes = 3*npixels;
124 
125   /* make sure image is more-or-less valid (and no overflows) */
126   if (isize <= 0 || w <= 0 || h <= 0 || npixels/w < h ||
127       nRGBbytes/3 < npixels || thePic.pm_cmtsize < 0)
128     return pmError(bname, "Bogus PM file!!");
129 
130   if (DEBUG)
131     fprintf(stderr,"%s: LoadPM() - loading a %dx%d %s pic, %d planes\n",
132 	    cmd, w, h, (thePic.pm_form==PM_I) ? "PM_I" : "PM_C",
133 	    thePic.pm_np);
134 
135 
136   /* allocate memory for picture and read it in */
137   thePic.pm_image = (char *) malloc((size_t) isize);
138   if (thePic.pm_image == NULL)
139     return( pmError(bname, "unable to malloc PM picture") );
140 
141   if (fread(thePic.pm_image, (size_t) isize, (size_t) 1, fp) != 1)   {
142     free(thePic.pm_image);
143     return( pmError(bname, "file read error") );
144   }
145 
146 
147   /* alloc and read in comment, if any */
148   if (thePic.pm_cmtsize>0) {
149     thePic.pm_cmt = (char *) malloc((size_t) thePic.pm_cmtsize+1);
150     if (thePic.pm_cmt) {
151       thePic.pm_cmt[thePic.pm_cmtsize] = '\0';  /* to be safe */
152       if (fread(thePic.pm_cmt,(size_t) thePic.pm_cmtsize,(size_t) 1,fp) != 1) {
153 	free(thePic.pm_cmt);
154 	thePic.pm_cmt = (char *) NULL;
155       }
156     }
157   }
158 
159   fclose(fp);
160 
161 
162   if (thePic.pm_form == PM_I) {
163     int  *intptr;
164     byte *pic24, *picptr;
165 
166     if ((pic24 = (byte *) malloc((size_t) nRGBbytes))==NULL) {
167       if (thePic.pm_cmt) free(thePic.pm_cmt);
168       return( pmError(bname, "unable to malloc 24-bit picture") );
169     }
170 
171     intptr = (int *) thePic.pm_image;
172     picptr = pic24;
173 
174     if (flipit) {    /* if flipit, integer is RRGGBBAA instead of AABBGGRR */
175       for (i=w*h; i>0; i--, intptr++) {
176 	if ((i & 0x3fff) == 0) WaitCursor();
177 	*picptr++ = (*intptr>>24) & 0xff;
178 	*picptr++ = (*intptr>>16) & 0xff;
179 	*picptr++ = (*intptr>>8)  & 0xff;
180       }
181     }
182     else {
183       for (i=w*h; i>0; i--, intptr++) {
184 	if ((i & 0x3fff) == 0) WaitCursor();
185 	*picptr++ = (*intptr)     & 0xff;
186 	*picptr++ = (*intptr>>8)  & 0xff;
187 	*picptr++ = (*intptr>>16) & 0xff;
188       }
189     }
190 
191     free(thePic.pm_image);
192 
193     pinfo->pic  = pic24;
194     pinfo->type = PIC24;
195   }
196 
197 
198   else if (thePic.pm_form == PM_C && thePic.pm_np>1) {
199     byte *pic24, *picptr, *rptr, *gptr, *bptr;
200 
201     if ((pic24 = (byte *) malloc((size_t) nRGBbytes))==NULL) {
202       if (thePic.pm_cmt) free(thePic.pm_cmt);
203       return( pmError(bname, "unable to malloc 24-bit picture") );
204     }
205 
206     rptr = (byte *) thePic.pm_image;
207     gptr = rptr + w*h;
208     bptr = rptr + w*h*2;
209     picptr = pic24;
210     for (i=w*h; i>0; i--) {
211       if ((i & 0x3fff) == 0) WaitCursor();
212       *picptr++ = *rptr++;
213       *picptr++ = *gptr++;
214       *picptr++ = *bptr++;
215     }
216     free(thePic.pm_image);
217 
218     pinfo->pic  = pic24;
219     pinfo->type = PIC24;
220   }
221 
222 
223   else if (thePic.pm_form == PM_C && thePic.pm_np==1) {
224     /* don't have to convert, just point pic at thePic.pm_image */
225     pic8 = (byte *) thePic.pm_image;
226     for (i=0; i<256; i++)
227       pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;  /* build mono cmap */
228 
229     pinfo->pic  = pic8;
230     pinfo->type = PIC8;
231   }
232 
233 
234   /* load up remaining pinfo fields */
235   pinfo->w = thePic.pm_ncol;  pinfo->h = thePic.pm_nrow;
236   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
237 
238   pinfo->frmType = F_PM;
239   pinfo->colType = (thePic.pm_form==PM_I || thePic.pm_np>1)
240                          ? F_FULLCOLOR : F_GREYSCALE;
241   sprintf(pinfo->fullInfo,"PM, %s.  (%d plane %s)  (%d bytes)",
242 	  (thePic.pm_form==PM_I || thePic.pm_np>1)
243 	        ? "24-bit color" : "8-bit greyscale",
244 	  thePic.pm_np, (thePic.pm_form==PM_I) ? "PM_I" : "PM_C",
245 	  isize + (int)PM_IOHDR_SIZE + thePic.pm_cmtsize);
246 
247   sprintf(pinfo->shrtInfo, "%dx%d PM.", w,h);
248   pinfo->comment = thePic.pm_cmt;
249 
250   return 1;
251 }
252 
253 
254 /*******************************************/
WritePM(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,comment)255 int WritePM(fp, pic, ptype, w, h, rmap, gmap, bmap, numcols, colorstyle,
256 	    comment)
257      FILE *fp;
258      byte *pic;
259      int   ptype,w,h;
260      byte *rmap, *gmap, *bmap;
261      int   numcols, colorstyle;
262      char *comment;
263 {
264   /* writes a PM file to the already open stream
265      'colorstyle' single-handedly determines the type of PM pic written
266      if colorstyle==0, (Full Color) a 3-plane PM_C pic is written
267      if colorstyle==1, (Greyscal) a 1-plane PM_C pic is written
268      if colorstyle==0, (B/W stipple) a 1-plane PM_C pic is written */
269 
270   char  foo[256];
271   int   i;
272   byte *p;
273 
274   /* create 'comment' field */
275   sprintf(foo,"CREATOR: XV %s\n", REVDATE);
276 
277   /* fill in fields of a pmheader */
278   thePic.pm_id = PM_MAGICNO;
279   thePic.pm_np = (colorstyle==0) ? 3 : 1;
280   thePic.pm_ncol = w;
281   thePic.pm_nrow = h;
282   thePic.pm_nband = 1;
283   thePic.pm_form  = PM_C;
284   thePic.pm_cmtsize = (strlen(foo) + 1);   /* +1 to write trailing '\0' */
285   if (comment) thePic.pm_cmtsize += (strlen(comment) + 1);
286 
287   putint32(thePic.pm_id, fp);
288   putint32(thePic.pm_np, fp);
289   putint32(thePic.pm_nrow, fp);
290   putint32(thePic.pm_ncol, fp);
291   putint32(thePic.pm_nband, fp);
292   putint32(thePic.pm_form, fp);
293   putint32(thePic.pm_cmtsize, fp);
294 
295   /* write the picture data */
296   if (colorstyle == 0) {         /* 24bit RGB, organized as 3 8bit planes */
297 
298     if (ptype == PIC8) {
299       WaitCursor();
300       for (i=0,p=pic; i<w*h; i++, p++) putc(rmap[*p], fp);
301 
302       WaitCursor();
303       for (i=0,p=pic; i<w*h; i++, p++) putc(gmap[*p], fp);
304 
305       WaitCursor();
306       for (i=0,p=pic; i<w*h; i++, p++) putc(bmap[*p], fp);
307     }
308 
309     else {  /* PIC24 */
310       WaitCursor();
311       for (i=0,p=pic; i<w*h; i++, p+=3) putc(*p, fp);
312 
313       WaitCursor();
314       for (i=0,p=pic+1; i<w*h; i++, p+=3) putc(*p, fp);
315 
316       WaitCursor();
317       for (i=0,p=pic+2; i<w*h; i++, p+=3) putc(*p, fp);
318     }
319   }
320 
321 
322   else if (colorstyle == 1) {    /* GreyScale: 8 bits per pixel */
323     byte rgb[256];
324 
325     if (ptype == PIC8) {
326       for (i=0; i<numcols; i++) rgb[i] = MONO(rmap[i],gmap[i],bmap[i]);
327       for (i=0, p=pic; i<w*h; i++, p++) {
328 	if ((i & 0x3fff) == 0) WaitCursor();
329 	putc(rgb[*p],fp);
330       }
331     }
332     else {  /* PIC24 */
333       for (i=0, p=pic; i<w*h; i++, p+=3) {
334 	if ((i & 0x3fff) == 0) WaitCursor();
335 	putc( MONO(p[0],p[1],p[2]), fp);
336       }
337     }
338   }
339 
340   else /* (colorstyle == 2) */ { /* B/W stipple.  pic is 1's and 0's */
341     /* note: pic has already been dithered into 8-bit image */
342     for (i=0, p=pic; i<w*h; i++, p++) {
343       if ((i & 0x3fff) == 0) WaitCursor();
344       putc(*p ? 255 : 0,fp);
345     }
346   }
347 
348   if (comment) {
349     fwrite(comment, (size_t) strlen(comment), (size_t) 1, fp);
350     fwrite("\n",    (size_t) 1,               (size_t) 1, fp);
351   }
352   fwrite(foo, strlen(foo) + 1, (size_t) 1, fp);  /* +1 for trailing '\0' */
353 
354   if (ferror(fp)) return -1;
355 
356   return 0;
357 }
358 
359 
360 /*****************************/
pmError(fname,st)361 static int pmError(fname, st)
362      const char *fname, *st;
363 {
364   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
365   if (thePic.pm_image != NULL) free(thePic.pm_image);
366   return 0;
367 }
368 
369 
370 /*****************************/
flip4(i)371 static int flip4(i)
372      int i;
373 {
374   /* flips low-order 4 bytes around in integer */
375 
376   byte b0, b1, b2, b3;
377   int  rv;
378 
379   b0 = (((u_long) i)) & 0xff;
380   b1 = (((u_long) i) >>  8) & 0xff;
381   b2 = (((u_long) i) >> 16) & 0xff;
382   b3 = (((u_long) i) >> 24) & 0xff;
383 
384   rv = (((u_long) b0) << 24) |
385        (((u_long) b1) << 16) |
386        (((u_long) b2) <<  8) |
387        (((u_long) b3));
388 
389   return rv;
390 }
391 
392 
393 
getint32(fp)394 static int getint32(fp)
395      FILE *fp;
396 {
397   int i;
398 
399   i  = (getc(fp) & 0xff) << 24;
400   i |= (getc(fp) & 0xff) << 16;
401   i |= (getc(fp) & 0xff) << 8;
402   i |= (getc(fp) & 0xff);
403 
404   return i;
405 }
406 
407 
408 
putint32(i,fp)409 static void putint32(i, fp)
410      int   i;
411      FILE *fp;
412 {
413   putc( ((i>>24) & 0xff), fp);
414   putc( ((i>>16) & 0xff), fp);
415   putc( ((i>> 8) & 0xff), fp);
416   putc( ((i) & 0xff), fp);
417 }
418 
419 
420 
421