1 /********************************************************************************
2 * *
3 * I R I S R G B I n p u t / O u t p u t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 2002,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
32 /*
33 Notes:
34 - Need to implement RLE compression some time.
35 - ARGB means Alpha(0), Red(1), Green(2), Blue(3) in memory.
36 - Uses a wee bit of memory during the load; but the advantage is that it
37 doesn't seek around on the file but loads all data sequentially.
38 - We now handle Luminance, Luminance/Alpha, RGB, and RGBA flavors of IRIS RGB.
39 */
40
41 using namespace FX;
42
43 /*******************************************************************************/
44
45 namespace FX {
46
47
48 #ifndef FXLOADRGB
49 extern FXAPI FXbool fxcheckRGB(FXStream& store);
50 extern FXAPI FXbool fxloadRGB(FXStream& store,FXColor*& data,FXint& width,FXint& height);
51 extern FXAPI FXbool fxsaveRGB(FXStream& store,const FXColor *data,FXint width,FXint height);
52 #endif
53
54
55 // RLE decompress with some checks against overruns
expand(FXuchar dst[],FXint dlen,const FXuchar src[],FXint slen)56 static void expand(FXuchar dst[],FXint dlen,const FXuchar src[],FXint slen){
57 FXuchar pixel,count;
58 FXint d=0,s=0;
59 while(s<slen){
60 pixel=src[s++];
61 count=pixel&0x7F;
62 if(count==0) break; // Normal termination with RLE 0-code
63 if(d+count>dlen) break; // Not enough space in destination
64 if(pixel&0x80){ // Literal bytes
65 if(s+count>slen) break; // Not enough bytes in source
66 while(count--){
67 dst[d++]=src[s++];
68 }
69 }
70 else{ // Repeated bytes
71 if(s+1>slen) break; // Not enough bytes in source
72 pixel=src[s++];
73 while(count--){
74 dst[d++]=pixel;
75 }
76 }
77 }
78 }
79
80
81 #if 0
82 static FXint compress(lbuf, rlebuf, z, cnt)
83 byte *lbuf, *rlebuf;
84 int z, cnt;
85
86
87
88 // RLE compress with some checks against overruns
89 static FXint compress(FXuchar dst[],FXint dlen,const FXuchar src[],FXint slen){
90 byte *iptr, *ibufend, *sptr, *optr;
91 short todo, cc;
92 long count;
93
94 lbuf += z;
95 iptr = lbuf;
96 ibufend = iptr+cnt*4;
97 optr = rlebuf;
98
99 while (iptr<ibufend) {
100 sptr = iptr;
101 iptr += 8;
102 while((iptr<ibufend) && ((iptr[-8]!=iptr[-4]) || (iptr[-4]!=iptr[0])))
103 iptr += 4;
104 iptr -= 8;
105
106 count = (iptr-sptr)/4;
107
108 while (count) {
109 todo = count>126 ? 126 : count;
110 count -= todo;
111 *optr++ = 0x80|todo;
112
113 while (todo--) {
114 *optr++ = *sptr;
115 sptr += 4;
116 }
117 }
118
119 sptr = iptr;
120 cc = *iptr;
121 iptr += 4;
122 while ((iptr<ibufend) && (*iptr == cc)) iptr += 4;
123
124 count = (iptr-sptr)/4;
125 while (count) {
126 todo = count>126 ? 126:count;
127 count -= todo;
128 *optr++ = todo;
129 *optr++ = cc;
130 }
131 }
132
133 *optr++ = 0;
134 return (optr - rlebuf);
135 }
136 #endif
137
138
139 // Convert planar grey scale to RGBA
bwtorgba(FXColor * l,const FXuchar * b,FXint n)140 static void bwtorgba(FXColor *l,const FXuchar *b,FXint n){
141 while(n--){
142 l[0]=FXRGB(b[0],b[0],b[0]);
143 l++;
144 b++;
145 }
146 }
147
148
149 // Convert planar luminance-alpha to interleaved RGBA
latorgba(FXColor * l,const FXuchar * b,const FXuchar * a,FXint n)150 static void latorgba(FXColor *l,const FXuchar *b,const FXuchar *a,FXint n){
151 while(n--){
152 l[0]=FXRGBA(b[0],b[0],b[0],a[0]);
153 l++;
154 b++;
155 a++;
156 }
157 }
158
159
160 // Convert planar rgb to interleaved RGBA
rgbtorgba(FXColor * l,const FXuchar * r,const FXuchar * g,const FXuchar * b,FXint n)161 static void rgbtorgba(FXColor *l,const FXuchar *r,const FXuchar *g,const FXuchar *b,FXint n){
162 while(n--){
163 l[0]=FXRGB(r[0],g[0],b[0]);
164 l++;
165 r++;
166 g++;
167 b++;
168 }
169 }
170
171
172 // Convert planar rgba to interleaved RGBA
rgbatorgba(FXColor * l,const FXuchar * r,const FXuchar * g,const FXuchar * b,const FXuchar * a,FXint n)173 static void rgbatorgba(FXColor *l,const FXuchar *r,const FXuchar *g,const FXuchar *b,const FXuchar *a,FXint n){
174 while(n--){
175 l[0]=FXRGBA(r[0],g[0],b[0],a[0]);
176 l++;
177 r++;
178 g++;
179 b++;
180 a++;
181 }
182 }
183
184
185 // Check if stream contains a RGB
fxcheckRGB(FXStream & store)186 FXbool fxcheckRGB(FXStream& store){
187 FXuchar signature[2];
188 store.load(signature,2);
189 store.position(-2,FXFromCurrent);
190 return signature[0]==0x01 && signature[1]==0xDA;
191 }
192
193
194 // Load image from stream
fxloadRGB(FXStream & store,FXColor * & data,FXint & width,FXint & height)195 FXbool fxloadRGB(FXStream& store,FXColor*& data,FXint& width,FXint& height){
196 FXlong base=store.position();
197 FXbool swap=store.swapBytes();
198 FXbool result=false;
199 FXushort magic;
200 FXuchar storage;
201 FXuchar bpc;
202 FXushort dimension;
203 FXushort w;
204 FXushort h;
205 FXushort channels;
206 FXuint maxpix;
207 FXuint minpix;
208 FXuint dummy;
209 FXchar name[80];
210 FXuint colormap;
211
212 // Null out
213 data=NULL;
214 width=0;
215 height=0;
216
217 // Remember swap state
218 store.setBigEndian(true);
219
220 // Load header
221 store >> magic; // MAGIC (2)
222 store >> storage; // STORAGE (1)
223 store >> bpc; // BPC (1)
224 store >> dimension; // DIMENSION (2)
225 store >> w; // XSIZE (2)
226 store >> h; // YSIZE (2)
227 store >> channels; // ZSIZE (2)
228 store >> minpix; // PIXMIN (4)
229 store >> maxpix; // PIXMAX (4)
230 store >> dummy; // DUMMY (4)
231 store.load(name,80); // IMAGENAME (80)
232 store >> colormap; // Colormap ID (4)
233
234 FXTRACE((100,"fxloadRGB: magic=%d name=%s width=%d height=%d nchannels=%d dimension=%d storage=%d bpc=%d\n",magic,name,w,h,channels,dimension,storage,bpc));
235
236 // Check magic number and other parameters
237 if(magic==474 && 1<=channels && channels<=4 && bpc==1 && 0<w && 0<h){
238 FXint tablen=h*channels; // Number of chunk start/chunk length table entries
239 FXint size=w*h; // Total number of pixels
240 FXint total=channels*size; // Total number of samples
241 FXuchar *planar;
242
243 // Skip to data
244 store.position(404,FXFromCurrent);
245
246 // Allocate planar array
247 if(allocElms(planar,total)){
248
249 // Allocate image data
250 if(allocElms(data,size)){
251 FXint i,j,k;
252
253 // Set width and height
254 width=w;
255 height=h;
256
257 // Compressed
258 if(storage){
259 FXuint *starttab;
260 FXuint *lengthtab;
261
262 // Allocate line tables
263 if(allocElms(starttab,tablen<<1)){
264 lengthtab=&starttab[tablen];
265
266 // Read line tables
267 store.load(starttab,tablen);
268 store.load(lengthtab,tablen);
269
270 // Offset of RLE chunks in the file
271 FXuint sub=store.position()-base;
272 FXuint chunklen=0;
273 FXuchar *chunk;
274
275 // Fix up the line table & figure space for RLE chunks
276 // Intelligent RGB writers (not ours ;-)) may re-use RLE
277 // chunks for more than 1 line...
278 for(i=0; i<tablen; i++){
279 starttab[i]-=sub;
280 chunklen=FXMAX(chunklen,(starttab[i]+lengthtab[i]));
281 }
282
283 // Make room for the compressed lines
284 if(allocElms(chunk,chunklen)){
285
286 // Load all RLE chunks in one fell swoop
287 store.load(chunk,chunklen);
288
289 // Decompress chunks into planar
290 for(k=0; k<tablen; ++k){
291 expand(&planar[k*width],width,&chunk[starttab[k]],lengthtab[k]);
292 }
293
294 // Free RLE chunks
295 freeElms(chunk);
296 result=true;
297 }
298
299 // Free line tables
300 freeElms(starttab);
301 }
302 }
303
304 // Uncompressed
305 else{
306 store.load(planar,total);
307 }
308
309 // Combine the channels properly
310 switch(channels){
311 case 1:
312 for(i=0,j=(height-1)*width; 0<=j; i+=width,j-=width){
313 bwtorgba(&data[i],&planar[j],width);
314 }
315 break;
316 case 2:
317 for(i=0,j=(height-1)*width; 0<=j; i+=width,j-=width){
318 latorgba(&data[i],&planar[j],&planar[j+size],width);
319 }
320 break;
321 case 3:
322 for(i=0,j=(height-1)*width; 0<=j; i+=width,j-=width){
323 rgbtorgba(&data[i],&planar[j],&planar[j+size],&planar[j+size+size],width);
324 }
325 break;
326 case 4:
327 for(i=0,j=(height-1)*width; 0<=j; i+=width,j-=width){
328 rgbatorgba(&data[i],&planar[j],&planar[j+size],&planar[j+size+size],&planar[j+size+size+size],width);
329 }
330 break;
331 }
332
333 // We're good
334 result=true;
335 }
336 freeElms(planar);
337 }
338 }
339
340 // Reset swap status
341 store.swapBytes(swap);
342 return result;
343 }
344
345
346 /*******************************************************************************/
347
348
349 // Save a bmp file to a stream
fxsaveRGB(FXStream & store,const FXColor * data,FXint width,FXint height)350 FXbool fxsaveRGB(FXStream& store,const FXColor *data,FXint width,FXint height){
351 FXushort magic=474;
352 FXuchar storage=0;
353 FXuchar bpc=1;
354 FXushort dimension=3;
355 FXushort w=width;
356 FXushort h=height;
357 FXushort channels=3;
358 FXuint maxpix=255;
359 FXuint minpix=0;
360 FXuint dummy=0;
361 FXuint colormap=0;
362 FXint size=width*height;
363 FXuchar temp[512];
364 FXuchar *array;
365 FXint i,j,k;
366 FXbool swap;
367
368 // Must make sense
369 if(data && 0<width && 0<height){
370
371 // Reorganize in planes
372 if(allocElms(array,size*channels)){
373
374 // Remember swap state
375 swap=store.swapBytes();
376 store.setBigEndian(true);
377
378 // Save header
379 store << magic; // MAGIC (2)
380 store << storage; // STORAGE (1)
381 store << bpc; // BPC (1)
382 store << dimension; // DIMENSION (2)
383 store << w; // XSIZE (2)
384 store << h; // YSIZE (2)
385 store << channels; // ZSIZE (2)
386 store << minpix; // PIXMIN (4)
387 store << maxpix; // PIXMAX (4)
388 store << dummy; // DUMMY (4)
389 memset(temp,0,80); // Clean it
390 memcpy(temp,"IRIS RGB",8); // Write name
391 store.save(temp,80); // IMAGENAME (80)
392 store << colormap; // COLORMAP (4)
393 memset(temp,0,404); // Clean it
394 store.save(temp,404); // DUMMY (404)
395
396 // Copy
397 for(j=height-1,k=0; j>=0; --j){
398 for(i=0; i<width; ++i,++k){
399 array[j*width+i]=((const FXuchar*)&data[k])[2];
400 array[j*width+i+size]=((const FXuchar*)&data[k])[1];
401 array[j*width+i+size+size]=((const FXuchar*)&data[k])[0];
402 }
403 }
404
405 // Save it
406 store.save(array,size*channels);
407
408 // Clean up temp memory
409 freeElms(array);
410
411 // Reset swap status
412 store.swapBytes(swap);
413 return true;
414 }
415 }
416 return false;
417 }
418
419 }
420