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