1 /*
2  * xvsunras.c - load routine for 'sun rasterfile' format pictures
3  *
4  * LoadSunRas(fname, numcols)  -  loads a PM pic, does 24to8 code if nec.
5  * WriteSunRas(fp, pic, w, h, r,g,b, numcols, style)
6  * WriteRaw(fp, pic, w, h, r,g,b, numcols, style)
7  *
8  * This file written by Dave Heath (heath@cs.jhu.edu)
9  * fixBGR() added by Ken Rossman (ken@shibuya.cc.columbia.edu)
10  */
11 
12 
13 #include "xv.h"
14 
15 /* info on sun rasterfiles taken from rasterfile(5) man page */
16 
17 #define   RAS_MAGIC 0x59a66a95
18 
19 struct rasterfile {
20      int  ras_magic;
21      int  ras_width;
22      int  ras_height;
23      int  ras_depth;
24      int  ras_length;
25      int  ras_type;
26      int  ras_maptype;
27      int  ras_maplength;
28 };
29 
30 #define RT_OLD          0       /* Raw pixrect image in 68000 byte order */
31 #define RT_STANDARD     1       /* Raw pixrect image in 68000 byte order */
32 #define RT_BYTE_ENCODED 2       /* Run-length compression of bytes */
33 #define RT_FORMAT_RGB   3       /* XRGB or RGB instead of XBGR or BGR */
34 
35 #define RMT_RAW		2
36 #define RMT_NONE	0
37 #define RMT_EQUAL_RGB	1
38 
39 #define RAS_RLE 0x80
40 
41 
42 static int  sunRasError    PARM((const char *, const char *));
43 static int  rle_read       PARM((byte *, int, int, FILE *, int));
44 static void sunRas1to8     PARM((byte *, byte *, int));
45 static void sunRas8to1     PARM((byte *, byte *, int, int));
46 static int  read_sun_long  PARM((int *, FILE *));
47 static int  write_sun_long PARM((int, FILE *));
48 static void fixBGR         PARM((unsigned char *, int, int));
49 
50 
51 /*******************************************/
LoadSunRas(fname,pinfo)52 int LoadSunRas(fname, pinfo)
53      char    *fname;
54      PICINFO *pinfo;
55 {
56   FILE	*fp;
57   int	 linesize,lsize,csize,isize,i,w,h,d,npixels,nbytes;
58   byte	 *image, *line;
59   struct rasterfile sunheader;
60   const char *bname;
61 
62   bname = BaseName(fname);
63 
64   /* read in the Sun Rasterfile picture */
65   fp = xv_fopen(fname,"r");
66   if (!fp) return( sunRasError(bname, "unable to open file") );
67 
68   read_sun_long (&sunheader.ras_magic	 , fp);
69   read_sun_long (&sunheader.ras_width	 , fp);
70   read_sun_long (&sunheader.ras_height	 , fp);
71   read_sun_long (&sunheader.ras_depth	 , fp);
72   read_sun_long (&sunheader.ras_length	 , fp);
73   read_sun_long (&sunheader.ras_type	 , fp);
74   read_sun_long (&sunheader.ras_maptype  , fp);
75   read_sun_long (&sunheader.ras_maplength, fp);
76 
77   if (sunheader.ras_magic != RAS_MAGIC) {
78     fclose(fp);
79     return( sunRasError(bname, "not a Sun rasterfile") );
80   }
81 
82 
83   /* make sure that the input picture can be dealt with */
84   if (sunheader.ras_depth != 1 &&
85       sunheader.ras_depth != 8 &&
86       sunheader.ras_depth != 24 &&
87       sunheader.ras_depth != 32) {
88     fprintf (stderr, "Sun rasterfile image has depth %d\n",
89 	     sunheader.ras_depth);
90     fprintf (stderr, "Depths supported are 1, 8, 24, and 32\n");
91     fclose(fp);
92     return 0;
93   }
94 
95 
96   if (sunheader.ras_type != RT_OLD &&
97       sunheader.ras_type != RT_STANDARD &&
98       sunheader.ras_type != RT_BYTE_ENCODED &&
99       sunheader.ras_type != RT_FORMAT_RGB) {
100     fprintf (stderr, "Sun rasterfile of unsupported type %d\n",
101 	     sunheader.ras_type);
102     fclose(fp);
103     return 0;
104   }
105 
106 
107   if (sunheader.ras_maptype != RMT_RAW &&
108       sunheader.ras_maptype != RMT_NONE &&
109       sunheader.ras_maptype != RMT_EQUAL_RGB) {
110     fprintf (stderr, "Sun rasterfile colormap of unsupported type %d\n",
111 	     sunheader.ras_maptype);
112     fclose(fp);
113     return 0;
114   }
115 
116   w = sunheader.ras_width;
117   h = sunheader.ras_height;
118   d = sunheader.ras_depth;  /* 1, 8, 24, or 32 (above) */
119   npixels = w * h;
120   if (w <= 0 || h <= 0 || npixels/w != h) {
121     fprintf (stderr, "Sun rasterfile image has invalid dimensions (%dx%d)\n",
122 	     w, h);
123     fclose(fp);
124     return 0;
125   }
126   if (d == 1)
127     nbytes = npixels/8;     /* should round up here, but used only for printf */
128   else {
129     nbytes = npixels * (d/8);
130 /*
131     [nbytes (isize) used only in printfs; don't really care about overflows]
132     if (nbytes/npixels != (d/8)) {
133       fprintf (stderr, "Sun rasterfile has invalid dimensions (%dx%dx%d)\n",
134 	       w, h, d);
135       fclose(fp);
136       return 0;
137     }
138  */
139   }
140   isize = sunheader.ras_length ? sunheader.ras_length : nbytes;
141   csize = (sunheader.ras_maptype == RMT_NONE) ? 0 : sunheader.ras_maplength;
142 
143 
144   /* length of the output (xv-format) image */
145   lsize = npixels;
146   if (d == 24 || d == 32) {
147     lsize *= 3;
148     if (lsize/3 != npixels) {
149       fprintf (stderr, "Sun rasterfile has invalid dimensions (%dx%dx%d)\n",
150 	       w, h, d);
151       fclose(fp);
152       return 0;
153     }
154   }
155 
156 
157   linesize = w * d;
158   if (linesize/w != d || linesize + 15 < linesize) {
159     fprintf (stderr, "Sun rasterfile has invalid dimensions (%dx%dx%d)\n",
160 	     w, h, d);
161     fclose(fp);
162     return 0;
163   }
164   if (linesize % 16) linesize += (16 - (linesize % 16));
165   linesize /= 8;
166 
167   if (DEBUG) {
168     fprintf(stderr,"%s: LoadSunRas() - loading a %dx%d pic, %d planes\n",
169 	    cmd, w, h, d);
170     fprintf (stderr,
171 	  "type %d, maptype %d, isize %d, csize %d, lsize %d, linesize %d\n",
172 	     sunheader.ras_type, sunheader.ras_maptype,
173 	     isize, csize, lsize, linesize);
174   }
175 
176 
177   /* read in the colormap, if any */
178   if (sunheader.ras_maptype == RMT_EQUAL_RGB && csize) {
179     fread (pinfo->r, (size_t) 1, (size_t) sunheader.ras_maplength/3, fp);
180     fread (pinfo->g, (size_t) 1, (size_t) sunheader.ras_maplength/3, fp);
181     fread (pinfo->b, (size_t) 1, (size_t) sunheader.ras_maplength/3, fp);
182   }
183 
184   else if (sunheader.ras_maptype == RMT_RAW && csize) {
185     /* we don't know how to handle raw colormap, ignore */
186     fseek (fp, (long) csize, 1);
187   }
188 
189   else {  /* no colormap, make one up */
190     if (sunheader.ras_depth == 1) {
191       pinfo->r[0] = pinfo->g[0] = pinfo->b[0] = 0;
192       pinfo->r[1] = pinfo->g[1] = pinfo->b[1] = 255;
193     }
194 
195     else if (sunheader.ras_depth == 8) {
196       for (i = 0; i < 256; i++)
197 	pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;
198     }
199   }
200 
201 
202   /* allocate memory for picture and read it in */
203   /* note we may slightly overallocate here (if image is padded) */
204   image = (byte *) malloc ((size_t) lsize);
205   line  = (byte *) malloc ((size_t) linesize);
206   if (image == NULL || line == NULL)
207     FatalError("Can't allocate memory for image\n");
208 
209 
210   for (i = 0; i < h; i++) {
211     if ((i&0x1f) == 0) WaitCursor();
212     if (sunheader.ras_type == RT_BYTE_ENCODED) {
213       if (rle_read (line, 1, linesize, fp, (i==0)) != linesize) break;
214     }
215 
216     else {
217       if (fread (line, (size_t) 1, (size_t) linesize, fp) != linesize) {
218 	free(image);  free(line);  fclose(fp);
219 	return (sunRasError (bname, "file read error"));
220       }
221     }
222 
223     switch (d) {
224     case 1:  sunRas1to8 (image + w * i, line, w);
225              break;
226     case 8:  xvbcopy((char *) line, (char *) image + w * i, (size_t) w);
227              break;
228     case 24: xvbcopy((char *) line, (char *) image + w * i * 3, (size_t) w*3);
229              break;
230 
231     case 32:
232       {
233 	int k;
234 	byte *ip, *op;
235 	ip = line;
236 	op = (byte *) (image + w * i * 3);
237 	for (k = 0; k<w; k++) {
238 	  ip++;            /* skip 'alpha' */
239 	  *op++ = *ip++;   /* red   */
240 	  *op++ = *ip++;   /* green */
241 	  *op++ = *ip++;   /* blue  */
242 	}
243       }
244     }
245   }
246 
247   free(line);
248 
249   if (DEBUG) fprintf(stderr,"Sun ras: image loaded!\n");
250 
251 
252 
253   if (d == 24 || d == 32) {
254     if (sunheader.ras_type != RT_FORMAT_RGB) fixBGR(image,w,h);
255     pinfo->type = PIC24;
256   }
257   else pinfo->type = PIC8;
258 
259   pinfo->pic = image;
260   pinfo->w = w;
261   pinfo->h = h;
262   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
263   pinfo->frmType = F_SUNRAS;
264   pinfo->colType = (d==1) ? F_BWDITHER : F_FULLCOLOR;
265   sprintf(pinfo->fullInfo, "Sun %s rasterfile.  (%d plane%s)  (%ld bytes)",
266 	  sunheader.ras_type == RT_BYTE_ENCODED ? "rle" : "standard",
267 	  d, d == 1 ? "" : "s",
268 	  (long) (sizeof(struct rasterfile) + csize + isize));
269 
270   sprintf(pinfo->shrtInfo, "%dx%d Sun Rasterfile.",w,h);
271   pinfo->comment = (char *) NULL;
272 
273   fclose(fp);
274   return 1;
275 }
276 
277 
278 /*****************************/
rle_read(ptr,size,nitems,fp,init)279 static int rle_read (ptr, size, nitems, fp, init)
280 byte *ptr;
281 int size, nitems,init;
282 FILE *fp;
283 {
284   static int count, ch;
285   int readbytes, c, read;
286 
287   if (init) { count = ch = 0; }
288 
289   readbytes = size * nitems;
290   for (read = 0; read < readbytes; read++) {
291     if (count) {
292       *ptr++ = (byte) ch;
293       count--;
294     }
295 
296     else {
297       c = getc(fp);
298       if (c == EOF) break;
299 
300       if (c == RAS_RLE) {   /* 0x80 */
301 	count = getc(fp);
302 	if (count == EOF) break;
303 
304 	if (count < 0) count &= 0xff;
305 	if (count == 0) *ptr++ = c;
306         else {
307           if ((ch = getc(fp)) == EOF) break;
308           *ptr++ = ch;
309         }
310       }
311       else *ptr++ = c;
312     }
313   }
314 
315   return (read/size);
316 }
317 
318 
319 /*****************************/
sunRasError(fname,st)320 static int sunRasError(fname, st)
321      const char *fname, *st;
322 {
323   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
324   return 0;
325 }
326 
327 
328 /************************************************/
sunRas1to8(dest,src,len)329 static void sunRas1to8 (dest, src, len)
330 byte *dest, *src;
331 int len;
332 {
333   int i, b;
334   int c = 0;
335 
336   for (i = 0, b = -1; i < len; i++) {
337     if (b < 0) {
338       b = 7;
339       c = ~*src++;
340     }
341     *dest++ = ((c >> (b--)) & 1);
342   }
343 }
344 
345 
346 
sunRas8to1(dest,src,len,flip)347 static void sunRas8to1 (dest, src, len, flip)
348 byte *dest, *src;
349 int len, flip;
350 {
351   int i, b;
352   int c;
353 
354   for (c = b = i = 0; i < len; i++) {
355     c <<= 1;
356     c |= (*src++ ? 1 : 0);
357     if (b++ == 7) {
358       if (flip) c = ~c;
359       *dest++ = (byte) (c & 0xff);
360       b = c = 0;
361     }
362   }
363   if (b) {
364     if (flip) c = ~c;
365     *dest = (byte) ((c<<(8-b)) & 0xff);
366   }
367 }
368 
369 
370 
371 
372 /*******************************************/
WriteSunRas(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,userle)373 int WriteSunRas(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,userle)
374      FILE *fp;
375      byte *pic;
376      int   ptype,w,h;
377      byte *rmap, *gmap, *bmap;
378      int   numcols, colorstyle, userle;
379 {
380   /* writes a sun rasterfile to the already open stream
381      writes either 24-bit, 8-bit or 1-bit
382      currently will not write rle files
383 
384      if PIC24 and F_GREYSCALE, writes an 8-bit grayscale image
385 
386      biggest problem w/ rle file: should we compute
387      image size first (nicer) or go back and write it
388      in when we are done (kludgy)?
389    */
390 
391   struct rasterfile sunheader;
392   int   linesize, i, color, d, y, flipbw;
393   byte *line, *graypic, graymap[256], *sp, *dp;
394 
395   graypic = NULL;
396   flipbw  = 0;
397 
398   /* special case: if PIC24 and writing GREYSCALE, write 8-bit file */
399   if (ptype == PIC24  && colorstyle == F_GREYSCALE) {
400     int npixels = w * h;
401     if (w <= 0 || h <= 0 || npixels/w != h) {
402       SetISTR(ISTR_WARNING, "Image is too large (%dx%d)", w, h);
403       return (2);
404     }
405     graypic = (byte *) malloc((size_t) npixels);
406     if (!graypic) FatalError("unable to malloc in WriteSunRas()");
407 
408     for (i=0,sp=pic,dp=graypic; i<npixels; i++,sp+=3,dp++) {
409       *dp = MONO(sp[0],sp[1],sp[2]);
410     }
411 
412     for (i=0; i<256; i++) graymap[i] = i;
413     rmap = gmap = bmap = graymap;
414     numcols = 256;
415     ptype = PIC8;
416     pic = graypic;
417   }
418 
419 
420   if (ptype==PIC24) {
421     d = 24;
422     linesize = w * 3;
423     if (linesize/w != 3) {
424       SetISTR(ISTR_WARNING, "Image is too wide (%d)", w);
425       if (graypic) free(graypic);
426       return (2);
427     }
428   } else if (colorstyle != F_BWDITHER) {
429     d = 8;
430     linesize = w;
431   } else {
432     d = 1;
433     linesize = w;
434     if (linesize % 8) linesize += (8 - linesize % 8);
435     linesize /= 8;
436   }
437 
438 
439 
440   if (linesize % 2) linesize++;
441   if (linesize == 0) {
442     SetISTR(ISTR_WARNING, "Image is too wide (%d)", w);
443     if (graypic) free(graypic);
444     return (2);
445   }
446   line = (byte *) malloc((size_t) linesize);
447   if (!line) {
448     SetISTR(ISTR_WARNING, "Can't allocate memory for save!\n");
449     if (graypic) free(graypic);
450     return (1);
451   }
452 
453   if (DEBUG)
454     fprintf (stderr,
455 	     "WriteSunRas: d %d, linesize %d numcols %d\n",
456 	     d, linesize, numcols);
457 
458   if (d==1) {
459     /* set flipbw if color#0 is black */
460     flipbw = (MONO(rmap[0],gmap[0],bmap[0]) < MONO(rmap[1],gmap[1],bmap[1]));
461   }
462 
463   /* set up the header */
464   sunheader.ras_magic	  = RAS_MAGIC;
465   sunheader.ras_width	  = w;
466   sunheader.ras_height	  = h;
467   sunheader.ras_depth	  = d;
468   sunheader.ras_length	  = linesize * h;
469   sunheader.ras_type	  = RT_STANDARD;
470   sunheader.ras_maptype   = (d==1 || d==24) ? RMT_NONE : RMT_EQUAL_RGB;
471   sunheader.ras_maplength = (d==1 || d==24) ? 0 : 3 * numcols;
472 
473   write_sun_long (sunheader.ras_magic	 , fp);
474   write_sun_long (sunheader.ras_width	 , fp);
475   write_sun_long (sunheader.ras_height	 , fp);
476   write_sun_long (sunheader.ras_depth	 , fp);
477   write_sun_long (sunheader.ras_length	 , fp);
478   write_sun_long (sunheader.ras_type	 , fp);
479   write_sun_long (sunheader.ras_maptype  , fp);
480   write_sun_long (sunheader.ras_maplength, fp);
481 
482   /* write the colormap */
483   if (d == 8) {
484     if (colorstyle == 1)  /* grayscale */
485       for (color=0; color<3; color++)
486 	for (i=0; i<numcols; i++)
487 	  putc (MONO(rmap[i],gmap[i],bmap[i]), fp);
488     else {
489       fwrite (rmap, sizeof(byte), (size_t) numcols, fp);
490       fwrite (gmap, sizeof(byte), (size_t) numcols, fp);
491       fwrite (bmap, sizeof(byte), (size_t) numcols, fp);
492     }
493   }
494 
495 
496   /* write the image */
497   line[linesize-1] = 0;
498   for (y = 0; y < h; y++) {
499     if ((y&0x1f) == 0) WaitCursor();
500 
501     if (d == 24) {
502       byte *lptr, *pix;
503 
504       for (i=0,lptr=line,pix=pic+y*w*3; i<w; i++,pix+=3) {
505 	*lptr++ = pix[2];          /* write date out in BGR order */
506 	*lptr++ = pix[1];
507 	*lptr++ = pix[0];
508       }
509     }
510 
511     else if (d == 8)
512       xvbcopy((char *) pic + y * w, (char *) line, (size_t) w);
513 
514     else /* d == 1 */
515       sunRas8to1 (line, pic + y * w, w, flipbw);
516 
517     if (fwrite (line, (size_t) 1, (size_t) linesize, fp) != linesize) {
518       SetISTR(ISTR_WARNING, "Write failed during save!\n");
519       if (graypic) free(graypic);
520       free(line);
521       return (2);
522     }
523   }
524 
525   free (line);
526   if (graypic) free(graypic);
527   return (0);
528 }
529 
530 
531 /* reads a 4-byte int in Sun byteorder
532    returns 0 for success, EOF for failure */
read_sun_long(l,fp)533 static int read_sun_long (l, fp)
534      int *l;
535      FILE *fp;
536 {
537   int c0, c1, c2, c3;
538 
539   c0 = fgetc(fp);
540   c1 = fgetc(fp);
541   c2 = fgetc(fp);
542   c3 = fgetc(fp);
543 
544   *l = (((u_long) c0 & 0xff) << 24) |
545        (((u_long) c1 & 0xff) << 16) |
546        (((u_long) c2 & 0xff) <<  8) |
547        (((u_long) c3 & 0xff));
548 
549   if (ferror(fp) || feof(fp)) return EOF;
550 
551   return 0;
552 }
553 
554 
555 /* write a long word in sun byte-order
556    returns 0 for success, EOF for failure
557  */
write_sun_long(l,fp)558 static int write_sun_long (l, fp)
559 int l;
560 FILE *fp;
561 {
562     char c;
563 
564     c = ((l >> 24) & 0xff);
565     if (putc (c, fp) == EOF) return (EOF);
566     c = ((l >> 16) & 0xff);
567     if (putc (c, fp) == EOF) return (EOF);
568     c = ((l >> 8) & 0xff);
569     if (putc (c, fp) == EOF) return (EOF);
570     c = (l & 0xff);
571     if (putc (c, fp) == EOF) return (EOF);
572     return (0);
573 }
574 
575 
576 
577 
578 /* kr3 - fix up BGR order SUN 24-bit rasters to be RGB order */
fixBGR(img,w,h)579 static void fixBGR(img,w,h)
580 unsigned char *img;
581 int w,h;
582 {
583   int i,npixels;
584   unsigned char tmp;
585 
586   npixels = w*h;
587   for (i=0; i<npixels; i++) {
588     tmp = img[0];                   /* swap red and blue channels */
589     img[0] = img[2];
590     img[2] = tmp;
591     img += 3;                       /* bump to next pixel */
592   }
593 }
594 
595