1 /********************************************************************************
2 *                                                                               *
3 *             S U N   R A S T E R   I M A G E   I n p u t / O u t p u t         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2004,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: fxrasio.cpp 4937 2019-03-10 19:59:30Z arthurcnorman $                         *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 
30 /*
31   Notes:
32 
33   - The official SUN Raster Image format specification says:
34 
35     A rasterfile is composed of three parts: first, a header containing 8
36     integers; second, a (possibly empty) set of colormap values; and third,
37     the pixel image, stored a line at a time, in increasing y order.
38     The image is layed out in the file as in a memory pixrect.  Each line of
39     the image is rounded up to the nearest 16 bits.
40 
41     The header is defined by the following structure:
42 
43          struct rasterfile {
44               int  ras_magic;
45               int  ras_width;
46               int  ras_height;
47               int  ras_depth;
48               int  ras_length;
49               int  ras_type;
50               int  ras_maptype;
51               int  ras_maplength;
52          };
53 
54     The ras_magic field always contains the following constant:
55 
56          #define   RAS_MAGIC 0x59a66a95
57 
58     The ras_width, ras_height, and ras_depth fields contain  the image's width
59     and height in pixels, and its depth in bits per pixel, respectively.
60     The depth is either 1 or 8, corresponding to standard frame buffer depths.
61     The ras_length field contains the length in bytes of the image data.
62     For an unencoded image, this number is computable from the ras_width,
63     ras_height, and ras_depth fields, but for an  encoded image it must be
64     explicitly stored in order to be available without decoding the image itself.
65 
66     Note: the  length of the header and of the (possibly empty) colormap values
67     are not included in the value of the ras_length field; it is only the
68     image data length.  For historical reasons, files of type RT_OLD will
69     usually have a 0 in the ras_length field, and software expecting to
70     encounter such files should be prepared to compute the  actual image data
71     length if needed.  The ras_maptype and ras_maplength fields contain the
72     type and length in bytes of the colormap values, respectively.
73     If ras_maptype is not RMT_NONE and the ras_maplength is not 0, then the
74     colormap values are the ras_maplength bytes immediately after the header.
75     These values are either uninterpreted bytes (usually with the ras_maptype
76     set to RMT_RAW) or the equal length red, green and blue vectors, in that
77     order (when the ras_maptype is RMT_EQUAL_RGB).
78     In the latter case, the ras_maplength must be three times the size in bytes
79     of any one of the vectors.
80 
81 
82   - A note from Jamie Zawinski says:
83 
84     The manpage for rasterfile(5) doesn't say anything about the format of
85     byte-encoded images, or  about plane/scanline  ordering in multi-plane
86     images.
87 
88     The first thing in the file is
89 
90             struct rasterfile {
91                     int ras_magic;
92                     int ras_width;
93                     int ras_height;
94                     int ras_depth;
95                     int ras_length;
96                     int ras_type;
97                     int ras_maptype;
98                     int ras_maplength;
99                     };
100 
101     The ras_magic field always contains the following constant:
102 
103             #define RAS_MAGIC 0x59a66a95
104 
105     The ras_length field is the length of the image data (which is the
106     length of the file minus the length of the header and colormap).
107     Catch: this is sometimes zero instead, so you can't really depend on
108     it.
109 
110     The ras_type field is ras_old=0, ras_standard=1, ras_byte_encoded=2,
111     or ras_experimental=FFFF.  There doesn't seem to be any difference
112     between OLD and STANDARD except that the ras_length field is always 0
113     in OLD.
114 
115     I didn't deal with cmaps, so from the  man page: "The ras_maptype and
116     ras_maplength fields contain the type and length in bytes of the
117     colormap values, respectively.  If ras_maptype is not RMT_NONE and the
118     ras_maplength is not 0, then the colormap values are the ras_maplength
119     bytes immediately after the header.   These values are either
120     uninterpreted bytes (usually with the ras_maptype set to RMT_RAW) or
121     the equal length red, green and blue vectors, in that order (when the
122     ras_maptype is RMT_EQUAL_RGB).  In the latter case, the ras_maplength
123     must be three times the size in bytes of any one of the vectors."
124     Regardless of width, the stored scanlines are rounded up to multiples
125     of 16 bits.
126 
127     I found the following description of byte-length encoding in Sun-Spots
128     Digest, Volume 6, Issue 84:
129 
130     > From:    jpm%green@lanl.gov (Pat McGee)
131     > Subject: Re: Format for byte encoded rasterfiles (1)
132     >
133     > The format is composed of many sequences of variable length records.
134     > Each record may be 1, 2, or 3 bytes long.
135     >
136     >  o  If the first byte is not 0x80, the record is one byte long, and
137     >     contains a pixel value.  Output 1 pixel of that value.
138     >  o  If the first byte is 0x80 and the second byte is zero, the record
139     >     is two bytes long.  Output 1 pixel with value 0x80.
140     >  o  If the first byte is 0x80, and the second byte is not zero, the
141     >     record is three bytes long.  The second byte is a count and the
142     >     third byte is a value.  Output (count+1) pixels of that value.
143     >
144     > A run is not terminated at the end of a scan line.  So, if there are
145     > three lines of red in a picture 100 pixels wide, the first run will
146     > be 0x80 0xff 0x<red>, and the second will be 0x80 0x2b 0x<red>.
147     >
148     > 	Pat McGee, jpm@lanl.gov
149 
150 */
151 
152 using namespace FX;
153 
154 /*******************************************************************************/
155 
156 namespace FX {
157 
158 
159 const FXint RAS_MAGIC = 0x59a66a95;     // Magic number
160 
161 const FXint RT_OLD          = 0;        // Raster types
162 const FXint RT_STANDARD     = 1;
163 const FXint RT_BYTE_ENCODED = 2;
164 const FXint RT_FORMAT_RGB   = 3;        // [X]RGB instead of [X]BGR
165 
166 const FXint RMT_NONE      = 0;          // Map type
167 const FXint RMT_EQUAL_RGB = 1;
168 const FXint RMT_RAW       = 2;
169 
170 
171 
172 struct HEADER {                         // File header
173   FXint magic;
174   FXint width;
175   FXint height;
176   FXint depth;
177   FXint length;
178   FXint type;
179   FXint maptype;
180   FXint maplength;
181   };
182 
183 
184 extern FXAPI bool fxcheckRAS(FXStream& store);
185 extern FXAPI bool fxloadRAS(FXStream& store,FXColor*& data,FXint& width,FXint& height);
186 extern FXAPI bool fxsaveRAS(FXStream& store,const FXColor *data,FXint width,FXint height);
187 
188 
189 // Read in MSB order
read32(FXStream & store)190 static inline FXuint read32(FXStream& store){
191   FXuchar c1,c2,c3,c4;
192   store >> c1 >> c2 >> c3 >> c4;
193   return ((FXuint)c4) | (((FXuint)c3)<<8) | (((FXuint)c2)<<16) | (((FXuint)c1)<<24);
194   }
195 
196 
197 // Check if stream contains a RAS
fxcheckRAS(FXStream & store)198 bool fxcheckRAS(FXStream& store){
199   FXint signature;
200   signature=read32(store);
201   store.position(-4,FXFromCurrent);
202   return signature==RAS_MAGIC;
203   }
204 
205 
206 // Load SUN raster image file format
fxloadRAS(FXStream & store,FXColor * & data,FXint & width,FXint & height)207 bool fxloadRAS(FXStream& store,FXColor*& data,FXint& width,FXint& height){
208   FXuchar red[256],green[256],blue[256],*line,count,c,*p,*q,bit;
209   FXint npixels,depth,linesize,x,y,i;
210   HEADER header;
211 
212   // Null out
213   data=NULL;
214   line=NULL;
215   width=0;
216   height=0;
217 
218   // Read header
219   header.magic=read32(store);
220   header.width=read32(store);
221   header.height=read32(store);
222   header.depth=read32(store);
223   header.length=read32(store);
224   header.type=read32(store);
225   header.maptype=read32(store);
226   header.maplength=read32(store);
227 
228   //FXTRACE((1,"fxloadRAS: magic=%08x width=%d height=%d depth=%d length=%d type=%d maptype=%d maplength=%d\n",header.magic,header.width,header.height,header.depth,header.length,header.type,header.maptype,header.maplength));
229 
230   // Check magic code
231   if(header.magic!=RAS_MAGIC) return false;
232 
233   // Trivial reject
234   if(header.width<1 || header.height<1) return false;
235 
236   // Bad colormap size
237   if(header.maplength<0 || header.maplength>768) return false;
238 
239   // Verify depth options; must be 1,8,24, or 32
240   if(header.depth!=1 && header.depth!=8 && header.depth!=24 && header.depth!=32) return false;
241 
242   // Verify supported types
243   if(header.type!=RT_OLD && header.type!=RT_STANDARD && header.type!=RT_BYTE_ENCODED && header.type!=RT_FORMAT_RGB) return false;
244 
245   // Verify map types
246   if(header.maptype!=RMT_RAW && header.maptype!=RMT_NONE && header.maptype!=RMT_EQUAL_RGB) return false;
247 
248   // Get size
249   width=header.width;
250   height=header.height;
251   depth=header.depth;
252   npixels=width*height;
253   linesize=((width*depth+15)/16)*2;
254 
255   //FXTRACE((1,"fxloadRAS: header.length=%d linesize=%d 4*npixels=%d\n",header.length,linesize,4*npixels));
256 
257   // Read in the colormap
258   if(header.maptype==RMT_EQUAL_RGB && header.maplength){
259     //FXTRACE((1,"fxloadRAS: RMT_EQUAL_RGB\n"));
260     store.load(red,header.maplength/3);
261     store.load(green,header.maplength/3);
262     store.load(blue,header.maplength/3);
263     }
264 
265   // Skip colormap
266   else if(header.maptype==RMT_RAW && header.maplength){
267     //FXTRACE((1,"fxloadRAS: RMT_RAW\n"));
268     store.position(header.maplength,FXFromCurrent);
269     }
270 
271   // Black and white
272   else if(header.depth==1){
273     //FXTRACE((1,"fxloadRAS: 1 bit\n"));
274     red[0]=green[0]=blue[0]=0;
275     red[1]=green[1]=blue[1]=255;
276     }
277 
278   // Gray scale
279   else if(header.depth==8){
280     //FXTRACE((1,"fxloadRAS: 8 bit\n"));
281     for(i=0; i<256; i++){
282       red[i]=green[i]=blue[i]=i;
283       }
284     }
285 
286   // Allocate pixel data
287   if(!FXMALLOC(&data,FXColor,npixels)) return false;
288 
289   // Allocate scanline
290   if(!FXMALLOC(&line,FXuchar,linesize)){ FXFREE(&data); return false; }
291 
292   // Now read the image
293   for(y=0,p=(FXuchar*)data,count=c=0; y<height; y++){
294     if(header.type!=RT_BYTE_ENCODED){           // Load uncompressed
295       store.load(line,linesize);
296       }
297     else{
298       for(i=0; i<linesize; i++){                // Load RLE compressed
299         if(count){
300           line[i]=c;
301           count--;
302           }
303         else{
304           store >> c;
305           if(c==0x80){
306             store >> count;
307             if(count==0){
308               line[i]=0x80;
309               }
310             else{
311               store >> c;
312               line[i]=c;
313               }
314             }
315           else{
316             line[i]=c;
317             }
318           }
319         }
320       }
321     if(depth==1){                               // 1 bits/pixel
322       for(x=0,q=line; x<width; x++,p+=4){
323 	bit=(line[x>>3]>>(7-(x&7)))&1;
324         p[0]=red[bit];
325         p[1]=green[bit];
326         p[2]=blue[bit];
327         p[3]=255;
328         }
329       }
330     else if(depth==8){                          // 8 bits/pixel
331       for(x=0,q=line; x<width; x++,p+=4,q+=1){
332         p[0]=red[q[0]];
333         p[1]=green[q[0]];
334         p[2]=blue[q[0]];
335         p[3]=255;
336 	}
337       }
338     else if(depth==24){                         // 24 bits/pixel
339       if(header.type==RT_FORMAT_RGB){
340         for(x=0,q=line; x<width; x++,p+=4,q+=3){
341           p[0]=q[0];
342           p[1]=q[1];
343           p[2]=q[2];
344           p[3]=255;
345           }
346         }
347       else{
348         for(x=0,q=line; x<width; x++,p+=4,q+=3){
349           p[0]=q[2];
350           p[1]=q[1];
351           p[2]=q[0];
352           p[3]=255;
353           }
354         }
355       }
356     else{                                       // 32 bits/pixel
357       if(header.type==RT_FORMAT_RGB){
358         for(x=0,q=line; x<width; x++,p+=4,q+=4){
359           p[0]=q[0];
360           p[1]=q[1];
361           p[2]=q[2];
362           p[3]=q[3];
363           }
364         }
365       else{
366         for(x=0,q=line; x<width; x++,p+=4,q+=4){
367           p[0]=q[2];
368           p[1]=q[1];
369           p[2]=q[0];
370           p[3]=q[3];
371           }
372         }
373       }
374     }
375 
376   // Release temporary stuff
377   FXFREE(&line);
378 
379   return true;
380   }
381 
382 
383 
384 
385 /*******************************************************************************/
386 
387 
388 // Write in MSB order
write32(FXStream & store,FXuint i)389 static inline void write32(FXStream& store,FXuint i){
390   FXuchar c1,c2,c3,c4;
391   c4=i&0xff;
392   c3=(i>>8)&0xff;
393   c2=(i>>16)&0xff;
394   c1=(i>>24)&0xff;
395   store << c1 << c2 << c3 << c4;
396   }
397 
398 
399 // Save SUN raster image file format
fxsaveRAS(FXStream & store,const FXColor * data,FXint width,FXint height)400 bool fxsaveRAS(FXStream& store,const FXColor *data,FXint width,FXint height){
401   FXint npixels=width*height;
402 
403   // Must make sense
404   if(!data || width<=0 || height<=0) return false;
405 
406   // Write header
407   write32(store,RAS_MAGIC);
408   write32(store,width);
409   write32(store,height);
410   write32(store,32);
411   write32(store,4*npixels);
412   write32(store,RT_FORMAT_RGB);
413   write32(store,RMT_NONE);
414   write32(store,0);
415 
416   // No RLE, or any other attempt to reduce size; sorry!
417   store.save(data,npixels);
418 
419   return true;
420   }
421 
422 
423 }
424