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,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (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                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXElement.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 #ifndef FXLOADRAS
185 extern FXAPI FXbool fxcheckRAS(FXStream& store);
186 extern FXAPI FXbool fxloadRAS(FXStream& store,FXColor*& data,FXint& width,FXint& height);
187 extern FXAPI FXbool fxsaveRAS(FXStream& store,const FXColor *data,FXint width,FXint height);
188 #endif
189 
190 
191 // Check if stream contains a RAS
fxcheckRAS(FXStream & store)192 FXbool fxcheckRAS(FXStream& store){
193   FXuchar signature[4];
194   store.load(signature,4);
195   store.position(-4,FXFromCurrent);
196   return signature[0]==0x59 && signature[1]==0xA6 && signature[2]==0x6A && signature[3]==0x95;
197   }
198 
199 
200 // Load SUN raster image file format
fxloadRAS(FXStream & store,FXColor * & data,FXint & width,FXint & height)201 FXbool fxloadRAS(FXStream& store,FXColor*& data,FXint& width,FXint& height){
202   FXuchar red[256],green[256],blue[256],*line,*p,*q,count,c,bit;
203   FXint   npixels,linesize,x,y,i;
204   HEADER  header;
205   FXbool  swap;
206   FXbool  ok=false;
207 
208   // Null out
209   data=NULL;
210   line=NULL;
211   width=0;
212   height=0;
213 
214   // Set big-endian
215   swap=store.swapBytes();
216   store.setBigEndian(true);
217 
218   // Read header
219   store >> header.magic;
220   store >> header.width;
221   store >> header.height;
222   store >> header.depth;
223   store >> header.length;
224   store >> header.type;
225   store >> header.maptype;
226   store >> header.maplength;
227 
228   FXTRACE((100,"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){
232 
233     // Verify depth options; must be 1,8,24, or 32
234     if(header.depth==1 || header.depth==8 || header.depth==24 || header.depth==32){
235 
236       // Verify supported types
237       if(header.type==RT_OLD || header.type==RT_STANDARD || header.type==RT_BYTE_ENCODED || header.type==RT_FORMAT_RGB){
238 
239         // Verify map types
240         if(header.maptype==RMT_RAW || header.maptype==RMT_NONE || header.maptype==RMT_EQUAL_RGB){
241 
242           // Bad colormap size
243           if(0<=header.maplength && header.maplength<=768){
244 
245             // Read in the colormap
246             if(header.maptype==RMT_EQUAL_RGB && header.maplength){
247               FXTRACE((100,"fxloadRAS: RMT_EQUAL_RGB\n"));
248               store.load(red,header.maplength/3);
249               store.load(green,header.maplength/3);
250               store.load(blue,header.maplength/3);
251               }
252 
253             // Skip colormap
254             else if(header.maptype==RMT_RAW && header.maplength){
255               FXTRACE((100,"fxloadRAS: RMT_RAW\n"));
256               store.position(header.maplength,FXFromCurrent);
257               }
258 
259             // Black and white
260             else if(header.depth==1){
261               FXTRACE((100,"fxloadRAS: 1 bit\n"));
262               red[0]=green[0]=blue[0]=0;
263               red[1]=green[1]=blue[1]=255;
264               }
265 
266             // Gray scale
267             else if(header.depth==8){
268               FXTRACE((100,"fxloadRAS: 8 bit\n"));
269               for(i=0; i<256; i++){
270                 red[i]=green[i]=blue[i]=i;
271                 }
272               }
273 
274             // Get sizes
275             linesize=((header.width*header.depth+15)/16)*2;
276             npixels=header.width*header.height;
277 
278             // Allocate scanline
279             if(allocElms(line,linesize)){
280 
281               // Allocate pixel data
282               if(allocElms(data,npixels)){
283 
284                 // Save size
285                 width=header.width;
286                 height=header.height;
287 
288                 FXTRACE((100,"fxloadRAS: header.length=%d linesize=%d 4*npixels=%d\n",header.length,linesize,4*npixels));
289 
290                 // Now read the image
291                 for(y=0,p=(FXuchar*)data,count=c=0; y<height; y++){
292                   if(header.type!=RT_BYTE_ENCODED){           // Load uncompressed
293                     store.load(line,linesize);
294                     }
295                   else{
296                     for(i=0; i<linesize; i++){                // Load RLE compressed
297                       if(count){
298                         line[i]=c;
299                         count--;
300                         }
301                       else{
302                         store >> c;
303                         if(c==0x80){
304                           store >> count;
305                           if(count==0){
306                             line[i]=0x80;
307                             }
308                           else{
309                             store >> c;
310                             line[i]=c;
311                             }
312                           }
313                         else{
314                           line[i]=c;
315                           }
316                         }
317                       }
318                     }
319                   if(header.depth==1){                          // 1 bits/pixel
320                     for(x=0,q=line; x<width; x++,p+=4){
321                       bit=(line[x>>3]>>(7-(x&7)))&1;
322                       p[0]=blue[bit];
323                       p[1]=green[bit];
324                       p[2]=red[bit];
325                       p[3]=255;
326                       }
327                     }
328                   else if(header.depth==8){                     // 8 bits/pixel
329                     for(x=0,q=line; x<width; x++,p+=4,q+=1){
330                       p[0]=blue[q[0]];
331                       p[1]=green[q[0]];
332                       p[2]=red[q[0]];
333                       p[3]=255;
334                       }
335                     }
336                   else if(header.depth==24){                    // 24 bits/pixel
337                     if(header.type==RT_FORMAT_RGB){
338                       for(x=0,q=line; x<width; x++,p+=4,q+=3){
339                         p[0]=q[2];
340                         p[1]=q[1];
341                         p[2]=q[0];
342                         p[3]=255;
343                         }
344                       }
345                     else{
346                       for(x=0,q=line; x<width; x++,p+=4,q+=3){
347                         p[0]=q[0];
348                         p[1]=q[1];
349                         p[2]=q[2];
350                         p[3]=255;
351                         }
352                       }
353                     }
354                   else{                                       // 32 bits/pixel
355                     if(header.type==RT_FORMAT_RGB){
356                       for(x=0,q=line; x<width; x++,p+=4,q+=4){
357                         p[0]=q[2];
358                         p[1]=q[1];
359                         p[2]=q[0];
360                         p[3]=q[3];
361                         }
362                       }
363                     else{
364                       for(x=0,q=line; x<width; x++,p+=4,q+=4){
365                         p[0]=q[0];
366                         p[1]=q[1];
367                         p[2]=q[2];
368                         p[3]=q[3];
369                         }
370                       }
371                     }
372                   }
373                 ok=true;
374                 }
375 
376               // Release temporary stuff
377               freeElms(line);
378               }
379             }
380           }
381         }
382       }
383     }
384   store.swapBytes(swap);
385   return ok;
386   }
387 
388 
389 
390 
391 /*******************************************************************************/
392 
393 
394 // Save SUN raster image file format
fxsaveRAS(FXStream & store,const FXColor * data,FXint width,FXint height)395 FXbool fxsaveRAS(FXStream& store,const FXColor *data,FXint width,FXint height){
396   const FXuchar *pp=(const FXuchar*)data;
397   HEADER header;
398   FXbool swap;
399 
400   // Must make sense
401   if(!data || width<=0 || height<=0) return false;
402 
403   // Set big-endian
404   swap=store.swapBytes();
405   store.setBigEndian(true);
406 
407   // Fill in header
408   header.magic=RAS_MAGIC;
409   header.width=width;
410   header.height=height;
411   header.depth=32;
412   header.length=4*width*height;
413   header.type=RT_FORMAT_RGB;
414   header.maptype=RMT_NONE;
415   header.maplength=0;
416 
417   // Write header
418   store << header.magic;
419   store << header.width;
420   store << header.height;
421   store << header.depth;
422   store << header.length;
423   store << header.type;
424   store << header.maptype;
425   store << header.maplength;
426 
427   // No RLE, or any other attempt to reduce size; sorry!
428   for(FXint i=0; i<width*height; i++,pp+=4){
429     store << pp[2]; // Red
430     store << pp[1]; // Green
431     store << pp[0]; // Blue
432     store << pp[3]; // Alpha
433     }
434   store.swapBytes(swap);
435   return true;
436   }
437 
438 
439 }
440