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