1 /*
2  * xvrle.c - load routine for rle (Utah Raster Toolkit) format pictures
3  *
4  * LoadRLE(fname, numcols)  -  loads an RLE file
5  */
6 
7 #include "copyright.h"
8 
9 #include "xv.h"
10 
11 
12 /* Definitions needed to parse RLE format */
13 /*   snarfed from rle_code.h, part of the Utah Raster Toolkit */
14 #define     LONG_OP             0x40
15 #define     RSkipLinesOp        1
16 #define     RSetColorOp         2
17 #define     RSkipPixelsOp       3
18 #define     RByteDataOp         5
19 #define     RRunDataOp          6
20 #define     REOFOp              7
21 
22 #define     H_CLEARFIRST        0x1   /* clear framebuffer flag */
23 #define     H_NO_BACKGROUND     0x2   /* if set, no bg color supplied */
24 #define     H_ALPHA             0x4   /* if set, alpha channel (-1) present */
25 #define     H_COMMENT           0x8   /* if set, comments present */
26 
27 
28 #define GETINT(fp) (c=getc(fp), c1=getc(fp), (c1<<8) + c )
29 
30 static void read_rle PARM((FILE *, byte *, int, int, int, int));
31 static int  rleError PARM((const char *, const char *));
32 
33 
34 
35 /*******************************************/
LoadRLE(fname,pinfo)36 int LoadRLE(fname, pinfo)
37      char    *fname;
38      PICINFO *pinfo;
39 /*******************************************/
40 {
41   FILE  *fp;
42   int    c, c1, i, j, k;
43   byte   bgcol[256];
44   byte   maps[3][256];
45   int    xpos, ypos, w, h, flags, ncolors, pixelbits, ncmap, cmaplen;
46   int    cmtlen;
47   byte  *img;
48   long filesize;
49   const char *bname;
50   const char *errstr;
51 
52   pinfo->type = PIC8;
53   pinfo->pic     = (byte *) NULL;
54   pinfo->comment = (char *) NULL;
55 
56   bname = BaseName(fname);
57 
58   /* open the stream */
59   fp = xv_fopen(fname,"r");
60   if (!fp) return (rleError(bname, "unable to open file"));
61 
62 
63   /* figure out the file size */
64   fseek(fp, 0L, 2);
65   filesize = ftell(fp);
66   fseek(fp, 0L, 0);
67 
68 
69   /* read the magic number */
70   c = getc(fp);  c1 = getc(fp);
71   if ((c != 0x52) || (c1 != 0xcc))
72     return(rleError(bname, "unrecognized magic number"));
73 
74 
75   xpos       = GETINT(fp);    /* read rest of header info */
76   ypos       = GETINT(fp);
77   w          = GETINT(fp);
78   h          = GETINT(fp);
79   flags      = getc(fp);
80   ncolors    = getc(fp);
81   pixelbits  = getc(fp);
82   ncmap      = getc(fp);
83   c          = getc(fp);
84   cmaplen = (1L << c);
85 
86   if (DEBUG) {
87     fprintf(stderr,"RLE: %dx%d image at %d,%d\n", w, h, xpos, ypos);
88     fprintf(stderr,"flags: 0x%02x  (%s%s%s%s)\n",
89 	    flags,
90 	    (flags & H_CLEARFIRST)    ? "CLEARFIRST " : "",
91 	    (flags & H_NO_BACKGROUND) ? "NO_BG " : "",
92 	    (flags & H_ALPHA)         ? "ALPHA " : "",
93 	    (flags & H_COMMENT)       ? "COMMENT" : "");
94 
95     fprintf(stderr, "%d bands, %d pixelbits, %d cmap bands, %d cmap entries\n",
96 	    ncolors, pixelbits, ncmap, cmaplen);
97   }
98 
99   if (!(flags & H_NO_BACKGROUND)) {
100     if (DEBUG) fprintf(stderr, "background value: ");
101     for (i=0; i<ncolors; i++) {
102       bgcol[i] = getc(fp);
103       if (DEBUG) fprintf(stderr, "0x%02x ", bgcol[i]);
104     }
105     if (DEBUG) fprintf(stderr,"\n");
106   }
107   else {
108     getc(fp);  /* skip filler byte */
109   }
110 
111   if ((ncolors % 2) == 0) getc(fp);     /* get on a word boundary */
112 
113   /* read colormap(s) */
114   for (i=0; i<ncmap; i++) {
115     for (j = 0; j<cmaplen; j++) {
116       k = GETINT(fp);
117       if (i<3) maps[i][j] = k>>8;
118     }
119   }
120 
121   if (DEBUG) {
122     if (ncmap) {
123       fprintf(stderr, "Colormap:\n");
124       for (i=0; i<cmaplen; i++) {
125 	fprintf(stderr,"(");
126 	for (j=0; (j<ncmap && j<3); j++) {
127 	  fprintf(stderr, "%02x ", maps[j][i]);
128 	}
129 	fprintf(stderr,")  ");
130       }
131       fprintf(stderr,"\n\n");
132     }
133     else fprintf(stderr,"No colormap\n");
134   }
135 
136 
137   /* read (skip over, actually) the comments, if any */
138   if (flags & H_COMMENT) {
139     cmtlen = GETINT(fp);
140     if (cmtlen) {
141       pinfo->comment = (char *) malloc((size_t) (cmtlen + 1));
142 
143       if (DEBUG) fprintf(stderr,"Comment: (%d bytes) '", cmtlen);
144       for (i=0; i<cmtlen; i++) {
145 	c = getc(fp);
146 	if (c==EOF) break;
147 	if (pinfo->comment) {
148 	  if (c == '\0') c = '\n';     /* translate NUL to NL */
149 	  pinfo->comment[i] = (char) c;
150 	}
151 	if (DEBUG) fprintf(stderr,"%c",c);
152       }
153       if (pinfo->comment) pinfo->comment[i] = '\0';
154 
155       if (cmtlen % 2) getc(fp);    /* get on a word boundary */
156       if (DEBUG) fprintf(stderr,"'\n\n");
157     }
158   }
159 
160 
161   if (ferror(fp) || feof(fp)) {
162     fclose(fp);
163     if (pinfo->comment) free(pinfo->comment);  pinfo->comment = (char *) NULL;
164     return rleError(bname, "EOF reached in RLE header.\n");
165   }
166 
167 
168   /*
169    * Acceptable cases:
170    *   ncolors = 1, 3, or >3 (extra planes ignored)
171    *   pixelbits = 8
172    *   ncmap = 0    (interpreted as TrueColor/TrueGray)
173    *           1    (TrueColor/TrueGray with a gamma curve)
174    *    3 | ncolors (TrueColor with three gamma curves, or
175    *                 PseudoColor if ncolors==1)
176    */
177 
178   errstr = NULL;
179   if (ncolors == 0 || ncolors == 2)
180     errstr = "Unsupt. # of channels in RLE file.\n";
181 
182   if (pixelbits != 8)
183     errstr = "Only 8-bit pixels supported in RLE files.\n";
184 
185   if (ncmap==0 || ncmap==1 || ncmap == 3 || ncmap == ncolors) { /* ok */ }
186   else errstr = "Invalid # of colormap channels in RLE file.\n";
187 
188   if (w<1 || h<1)
189     errstr = "Bogus size in RLE header.\n";
190 
191 
192   if (errstr) {
193     fclose(fp);
194     if (pinfo->comment) free(pinfo->comment);  pinfo->comment = (char *) NULL;
195     return rleError(bname, errstr);
196   }
197 
198 
199   /* allocate image memory */
200   if (ncolors == 1) img = (byte *) calloc((size_t) w * h,     (size_t) 1);
201                else img = (byte *) calloc((size_t) w * h * 3, (size_t) 1);
202   if (!img) {
203     fclose(fp);
204     if (pinfo->comment) free(pinfo->comment);  pinfo->comment = (char *) NULL;
205     return rleError(bname, "unable to allocate image data.\n");
206   }
207 
208 
209   /* set background, if necessary */
210   if ((flags & H_CLEARFIRST) && !(flags & H_NO_BACKGROUND)) {
211     byte *ip;
212     if (ncolors == 1) {
213       for (i=0, ip=img; i<w*h; i++, ip++) *ip = bgcol[0];
214     }
215     else {
216       for (i=0, ip=img; i<w*h; i++)
217 	for (j=0; j<3; j++, ip++) *ip = bgcol[j];
218     }
219   }
220 
221 
222   read_rle(fp, img, w, h, ncolors, ncmap);
223 
224   if (ferror(fp) || feof(fp))    /* just a warning */
225     rleError(bname, "RLE file appears to be truncated.");
226 
227   fclose(fp);
228 
229 
230   /* apply gamma curve(s) to image (if grayscale or truecolor) */
231   if (ncmap) {
232     byte *ip;
233     int   imagelen, cmask;
234     imagelen = (ncolors==1) ? w*h : w*h*3;
235     cmask = (cmaplen-1);
236 
237     if (ncmap == 1) {   /* single gamma curve */
238       for (i=0, ip=img; i<imagelen; i++, ip++) *ip = maps[0][*ip & cmask];
239     }
240 
241     else if (ncmap >= 3 && ncolors >=3) {   /* one curve per band */
242       for (i=0, ip=img; i<w*h; i++) {
243 	*ip = maps[0][*ip & cmask];   ip++;
244 	*ip = maps[1][*ip & cmask];   ip++;
245 	*ip = maps[2][*ip & cmask];   ip++;
246       }
247     }
248   }
249 
250 
251   /* finally, convert into XV internal format */
252 
253   pinfo->pic = img;
254   pinfo->w   = w;
255   pinfo->h   = h;
256   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
257   pinfo->frmType = -1;    /* no default format to save in */
258 
259   if (ncolors == 1) {     /* grayscale or PseudoColor */
260     pinfo->type = PIC8;
261     if (ncmap == 0 || ncmap == 1) {   /* grey, or grey with gamma curve */
262       pinfo->colType = F_GREYSCALE;
263       sprintf(pinfo->fullInfo, "Greyscale RLE.  (%ld bytes)", filesize);
264       for (i=0; i<256; i++)
265 	pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;
266     }
267     else {
268       pinfo->colType = F_FULLCOLOR;
269       sprintf(pinfo->fullInfo, "PseudoColor RLE.  (%ld bytes)", filesize);
270       for (i=0; i<256; i++) {
271 	pinfo->r[i] = maps[0][i];
272 	pinfo->g[i] = maps[1][i];
273 	pinfo->b[i] = maps[2][i];
274       }
275     }
276 
277     sprintf(pinfo->shrtInfo, "%dx%d RLE.",w, h);
278   }
279 
280   else {                  /* true color */
281     pinfo->type = PIC24;
282     pinfo->colType = F_FULLCOLOR;
283     sprintf(pinfo->fullInfo, "TrueColor RLE.  (%ld bytes)", filesize);
284     sprintf(pinfo->shrtInfo, "%dx%d RLE.", w, h);
285   }
286 
287   return 1;
288 }
289 
290 
291 /*******************************************/
read_rle(fp,img,w,h,ncolors,ncmap)292 static void read_rle(fp, img, w, h, ncolors, ncmap)
293      FILE *fp;
294      byte *img;
295      int   w, h, ncolors, ncmap;
296 {
297   int posx, posy, plane, bperpix, i, pixval, skipcalls;
298   int opcode, operand, done, c, c1;
299   byte *ip;
300 
301   posx = posy = plane = done = skipcalls = 0;
302   if (ncolors == 1) bperpix = 1;
303                else bperpix = 3;
304 
305 
306   while (!done && (opcode=getc(fp)) != EOF) {
307     switch (opcode & 0x3f) {
308     case RSkipLinesOp:
309       if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
310       else operand = getc(fp);
311       posx = 0;
312       posy += operand;
313       skipcalls++;
314       if ((skipcalls & 0x7f)==0) WaitCursor();
315       break;
316 
317 
318     case RSetColorOp:
319       operand = getc(fp);
320       plane = operand;
321       posx = 0;
322       break;
323 
324 
325     case RSkipPixelsOp:
326       if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
327       else operand = getc(fp);
328 
329       posx += operand;
330       break;
331 
332 
333     case RByteDataOp:
334       if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
335       else operand = getc(fp);
336 
337       ip = img + ((h-posy-1) * w*bperpix) + posx*bperpix + plane;
338       operand++;
339 
340       for (i=0; i<operand; i++, ip+=bperpix) {
341 	c = getc(fp);
342 	if (plane<ncolors && posy<h && (posx+i < w)) *ip = c;
343       }
344 
345       if (operand & 1) getc(fp);  /* word boundary */
346       posx += operand;
347       break;
348 
349 
350     case RRunDataOp:
351       if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
352       else operand = getc(fp);
353 
354       pixval = getc(fp);  getc(fp);
355       operand++;
356 
357       ip = img + ((h-posy-1) * w*bperpix) + posx*bperpix + plane;
358 
359       for (i=0; i<operand; i++, ip+=bperpix) {
360 	if (plane<ncolors && posy<h && (posx+i < w)) *ip = pixval;
361       }
362 
363       /*  if (operand & 1) getc(fp); */  /* word boundary */
364       posx += operand;
365       break;
366 
367     case REOFOp:
368       done = 1;
369       break;
370     }
371   }
372 }
373 
374 
375 /*******************************************/
rleError(fname,st)376 static int rleError(fname,st)
377      const char *fname, *st;
378 {
379   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
380   return 0;
381 }
382 
383