1 /********************************************************************************
2 *                                                                               *
3 *                          B M P   I n p u t / O u t p u t                      *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,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 "fxendian.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXElement.h"
29 #include "FXStream.h"
30 
31 /*
32   Notes:
33   - Writer should use fxezquantize() and if the number of colors is less than
34     256, use 8bpp RLE compressed output; if less that 4, use 4bpp RLE compressed
35     output, else if less than 2, use monochrome.
36   - Writer should do this only when no loss of fidelity occurs.
37   - Find documentation on 32-bpp bitmap.
38   - Need to have checks in RLE decoder for out-of-bounds checking.
39   - To map from 5-bit to 8-bit, we use value*8+floor(value/4) which
40     is almost the same as the correct value*8.225806.
41 */
42 
43 // Bitmap compression values
44 #define BIH_RGB       0         // RGB mode
45 #define BIH_RLE8      1         // 8-bit/pixel rle mode
46 #define BIH_RLE4      2         // 4-bit/pixel rle mode
47 #define BIH_BITFIELDS 3         // Bit field mode
48 #define BIH_JPEG      4         // Not supported
49 #define BIH_PNG       5         // Not supported
50 
51 #define RLE_ESC       0         // RLE escape sequence
52 #define RLE_LINE      0         // RLE end of line
53 #define RLE_END       1         // RLE end of bitmap
54 #define RLE_DELTA     2         // RLE delta
55 
56 #define OS2_OLD       12        // Bitmap Info Header sizes
57 #define OS2_NEW       64
58 #define WIN_NEW       40
59 
60 #define IDH_ICO       1         // ICO
61 #define IDH_CUR       2         // CUR
62 
63 
64 using namespace FX;
65 
66 /*******************************************************************************/
67 
68 namespace FX {
69 
70 
71 // Check BMP, ICO/CUR file based on contents
72 extern FXAPI FXbool fxcheckBMP(FXStream& store);
73 extern FXAPI FXbool fxcheckICO(FXStream& store);
74 
75 // Load / save BMP
76 extern FXAPI FXbool fxloadBMP(FXStream& store,FXColor*& data,FXint& width,FXint& height);
77 extern FXAPI FXbool fxsaveBMP(FXStream& store,const FXColor *data,FXint width,FXint height);
78 
79 // Load / save DIB
80 extern FXAPI FXbool fxloadDIB(FXStream& store,FXColor*& data,FXint& width,FXint& height);
81 extern FXAPI FXbool fxsaveDIB(FXStream& store,const FXColor *data,FXint width,FXint height);
82 
83 // Load / save ICO or CUR
84 extern FXAPI FXbool fxloadICO(FXStream& store,FXColor*& data,FXint& width,FXint& height,FXint& xspot,FXint& yspot);
85 extern FXAPI FXbool fxsaveICO(FXStream& store,const FXColor *data,FXint width,FXint height,FXint xspot=-1,FXint yspot=-1);
86 
87 extern FXAPI FXbool fxloadICOStream(FXStream& store,FXColor*& data,FXint& width,FXint& height);
88 
89 
90 // Bitmap Info Header
91 struct BitmapInfoHeader {
92   FXuint   biSize;
93   FXint    biWidth;
94   FXint    biHeight;
95   FXushort biPlanes;
96   FXushort biBitCount;
97   FXuint   biCompression;
98   FXuint   biSizeImage;
99   FXint    biXPelsPerMeter;
100   FXint    biYPelsPerMeter;
101   FXuint   biClrUsed;
102   FXuint   biClrImportant;
103   };
104 
105 
106 // Bitmap File Header
107 struct BitmapFileHeader {
108   FXushort bfType;              // BM
109   FXuint   bfSize;
110   FXushort bfReserved1;
111   FXushort bfReserved2;
112   FXuint   bfOffBits;
113   };
114 
115 
116 // Icon Directory
117 struct IconDirectory {
118   FXushort idReserved;          // Must be 0
119   FXushort idType;              // ICO=1, CUR=2
120   FXushort idCount;             // Number of images
121   };
122 
123 
124 // Icon Directory Entry
125 struct IconDirectoryEntry {
126   FXuchar  bWidth;
127   FXuchar  bHeight;
128   FXuchar  bColorCount;
129   FXuchar  bReserved;
130   FXushort wXHotspot;           // X hotspot if cursor, #planes if icon
131   FXushort wYHotspot;           // Y hotspot if cursor, #bits/pixel if icon
132   FXuint   dwBytesInRes;
133   FXuint   dwImageOffset;
134   };
135 
136 /*******************************************************************************/
137 
138 // Check if stream contains a BMP
fxcheckBMP(FXStream & store)139 FXbool fxcheckBMP(FXStream& store){
140   FXuchar signature[2];
141   store.load(signature,2);
142   store.position(-2,FXFromCurrent);
143   return signature[0]=='B' && signature[1]=='M';
144   }
145 
146 
147 // Check if stream contains ICO or CUR
fxcheckICO(FXStream & store)148 FXbool fxcheckICO(FXStream& store){
149   FXbool swap=store.swapBytes();
150   FXshort signature[3];
151   store.setBigEndian(false);
152   store.load(signature,3);
153   store.position(-6,FXFromCurrent);
154   store.swapBytes(swap);
155   return signature[0]==0 && (signature[1]==IDH_ICO || signature[1]==IDH_CUR) && signature[2]>=1;
156   }
157 
158 /*******************************************************************************/
159 
160 // Load bitmap bits
fxloadBMPBits(FXStream & store,FXColor * & data,FXint width,FXint height,FXint bpp,FXint enc,FXint clrs,FXint fmt)161 static FXbool fxloadBMPBits(FXStream& store,FXColor*& data,FXint width,FXint height,FXint bpp,FXint enc,FXint clrs,FXint fmt){
162   if(allocElms(data,width*height)){
163     FXColor  colormap[256],c1,c2;
164     FXuchar  padding[4],r,g,b,a;
165     FXint    pad,i,x,y;
166     FXushort rgb16;
167 
168     // Otherwise, maybe a map
169     if(bpp<=8){
170 
171       // OS2 has 3-byte colormaps
172       if(fmt==3){
173         for(i=0; i<clrs; i++){
174           store >> b;                           // Blue
175           store >> g;                           // Green
176           store >> r;                           // Red
177           colormap[i]=FXRGB(r,g,b);
178           }
179         }
180 
181       // Microsoft bitmaps have 4-byte colormaps
182       else{
183         for(i=0; i<clrs; i++){
184           store >> b;                           // Blue
185           store >> g;                           // Green
186           store >> r;                           // Red
187           store >> a;
188           colormap[i]=FXRGB(r,g,b);
189           }
190         }
191       }
192 
193     // Handle various depths
194     switch(bpp){
195       case 1:                                   // 1-bit/pixel
196         pad=(width+31)&~31;                     // Padded to DWORD
197         for(y=height-1; y>=0; y--){
198           for(x=0; x<pad; x++){
199             if((x&7)==0){ store >> b; }
200             if(__unlikely(x>=width)) continue;
201             data[y*width+x]=colormap[(b>>7)&1];
202             b<<=1;
203             }
204           }
205         return true;
206       case 4:                                   // 4-bit/pixel
207         if(enc==BIH_RLE4){                      // Read RLE4 compressed data
208           x=0;
209           y=height-1;
210           while(!store.eof()){
211             store >> a;
212             store >> b;
213             if(a==RLE_ESC){                     // Escape code
214               if(b==RLE_END){                   // End of data
215                 break;
216                 }
217               if(b==RLE_LINE){                  // End of line
218                 x=0;
219                 y--;
220                 continue;
221                 }
222               if(b==RLE_DELTA){                 // Delta
223                 store >> a; x+=a;
224                 store >> a; y-=a;
225                 continue;
226                 }
227               if(__unlikely(y<0)) break;        // Safety check
228               for(i=0; i<b; ++i){               // Absolute mode
229                 if(i&1){
230                   c1=colormap[a&15];
231                   }
232                 else{
233                   store >> a;
234                   c1=colormap[a>>4];
235                   }
236                 if(__unlikely(x>=width)) continue;
237                 data[y*width+x++]=c1;
238                 }
239               if(((b&3)==1) || ((b&3)==2)) store >> a;          // Read pad byte
240               }
241             else{                               // Repeat mode
242               if(__unlikely(y<0)) break;        // Safety check
243               c1=colormap[b>>4];
244               c2=colormap[b&15];
245               for(i=0; i<a && x<width; ++i){
246                 data[y*width+x++]=(i&1)?c2:c1;
247                 }
248               }
249             }
250           }
251         else{                                   // Read uncompressed data
252           pad=(width+7)&~7;                     // Padded to DWORD
253           for(y=height-1; y>=0; y--){
254             for(x=0; x<pad; x+=2){
255               store >> a;
256               if(__unlikely(x>=width)) continue;
257               data[y*width+x]=colormap[a>>4];
258               if(__unlikely(x+1>=width)) continue;
259               data[y*width+x+1]=colormap[a&15];
260               }
261             }
262           }
263         return true;
264       case 8:                                   // 8-bit/pixel
265         if(enc==BIH_RLE8){                      // Read RLE8 compressed data
266           x=0;
267           y=height-1;
268           while(!store.eof()){
269             store >> a;
270             store >> b;
271             if(a==RLE_ESC){                     // Escape code
272               if(b==RLE_END){                   // End of data
273                 break;
274                 }
275               if(b==RLE_LINE){                  // End of line
276                 x=0;
277                 y--;
278                 continue;
279                 }
280               if(b==RLE_DELTA){                 // Delta
281                 store >> a; x+=a;
282                 store >> a; y-=a;
283                 continue;
284                 }
285               if(__unlikely(y<0)) break;        // Safety check
286               for(i=0; i<b && x<width; ++i){    // Absolute mode
287                 store >> a;
288                 data[y*width+x++]=colormap[a];
289                 }
290               if(b&1) store >> a;               // Odd length run: read an extra pad byte
291               }
292             else{                               // Repeat mode
293               if(__unlikely(y<0)) break;        // Safety check
294               c1=colormap[b];
295               for(i=0; i<a && x<width; ++i){
296                 data[y*width+x++]=c1;
297                 }
298               }
299             }
300           }
301         else{                                   // Read uncompressed data
302           pad=(4-(width&3))&3;                  // Padded to DWORD
303           for(y=height-1; y>=0; y--){
304             for(x=0; x<width; x++){
305               store >> a;
306               data[y*width+x]=colormap[a];
307               }
308             store.load(padding,pad);
309             }
310           }
311         return true;
312       case 16:                                  // 16-bit/pixel
313         pad=(4-((width*2)&3))&3;                // Padded to DWORD
314         for(y=height-1; y>=0; y--){
315           for(x=0; x<width; x++){
316             store >> rgb16;
317             r=((rgb16<<3)&0xf8)+((rgb16>> 2)&7);
318             g=((rgb16>>2)&0xf8)+((rgb16>> 7)&7);
319             b=((rgb16>>7)&0xf8)+((rgb16>>12)&7);
320             data[y*width+x]=FXRGB(r,g,b);
321             }
322           store.load(padding,pad);
323           }
324         return true;
325       case 24:                                  // 24-bit/pixel
326         pad=(4-((width*3)&3))&3;                // Padded to DWORD
327         for(y=height-1; y>=0; y--){
328           for(x=0; x<width; x++){
329             store >> b;
330             store >> g;
331             store >> r;
332             data[y*width+x]=FXRGB(r,g,b);
333             }
334           store.load(padding,pad);
335           }
336         return true;
337       case 32:                                  // 32-bit/pixel
338         for(y=height-1; y>=0; y--){
339           for(x=0; x<width; x++){
340             store >> b;
341             store >> g;
342             store >> r;
343             store >> a;
344             data[y*width+x]=FXRGBA(r,g,b,a);
345             }
346           }
347         return true;
348       }
349     }
350   return false;
351   }
352 
353 
354 // Save bitmap bits
fxsaveBMPBits(FXStream & store,const FXColor * data,FXint width,FXint height,FXint bpp)355 static FXbool fxsaveBMPBits(FXStream& store,const FXColor* data,FXint width,FXint height,FXint bpp){
356   const FXuchar padding[3]={0,0,0};
357   FXuchar pad,r,g,b,a;
358   FXint x,y;
359 
360   // Handle various depths
361   switch(bpp){
362     case 24:                                    // 24-bit/pixel
363       pad=(4-((width*3)&3))&3;                  // Padded to DWORD
364       for(y=height-1; y>=0; y--){
365         for(x=0; x<width; x++){
366           r=FXREDVAL(data[y*width+x]);
367           g=FXGREENVAL(data[y*width+x]);
368           b=FXBLUEVAL(data[y*width+x]);
369           store << b;
370           store << g;
371           store << r;
372           }
373         store.save(padding,pad);
374         }
375       return true;
376     case 32:                                    // 32-bit/pixel
377       for(y=height-1; y>=0; y--){
378         for(x=0; x<width; x++){
379           r=FXREDVAL(data[y*width+x]);
380           g=FXGREENVAL(data[y*width+x]);
381           b=FXBLUEVAL(data[y*width+x]);
382           a=FXALPHAVAL(data[y*width+x]);
383           store << b;
384           store << g;
385           store << r;
386           store << a;
387           }
388         }
389       return true;
390     }
391   return false;
392   }
393 
394 /*******************************************************************************/
395 
396 // Load icon bits
fxloadICOBits(FXStream & store,FXColor * & data,FXint width,FXint height,FXint bpp,FXint enc,FXint clrs,FXint fmt)397 static FXbool fxloadICOBits(FXStream& store,FXColor*& data,FXint width,FXint height,FXint bpp,FXint enc,FXint clrs,FXint fmt){
398   FXint x,y,pad; FXuchar c;
399 
400   // Load pixels (XOR bytes)
401   if(fxloadBMPBits(store,data,width,height,bpp,enc,clrs,fmt)){
402 
403     // Use AND bytes to set alpha channel
404     if(bpp<32){
405       pad=(4-((width+7)>>3))&3;         // Padded to DWORD
406       for(y=height-1; y>=0; y--){
407         for(x=0; x<width; x++){
408           if((x&7)==0){ store >> c; }
409           if(c&0x80) data[y*width+x]&=FXRGBA(255,255,255,0);
410           c<<=1;
411           }
412         store.position(pad,FXFromCurrent);
413         }
414       }
415 
416     // Got alpha, so skip over AND bytes
417     else{
418       pad=((width+31)>>5)<<2;           // Width rounded up to DWORD
419       store.position(height*pad,FXFromCurrent);
420       }
421     return true;
422     }
423   return false;
424   }
425 
426 
427 // Save icon bits
fxsaveICOBits(FXStream & store,const FXColor * data,FXint width,FXint height,FXint bpp)428 static FXbool fxsaveICOBits(FXStream& store,const FXColor* data,FXint width,FXint height,FXint bpp){
429   const FXuchar padding[3]={0,0,0};
430   FXint x,y,pad; FXuchar c,bit;
431 
432   // Save pixels (XOR bytes)
433   if(fxsaveBMPBits(store,data,width,height,bpp)){
434 
435     // Write AND bytes from alpha channel
436     pad=(4-((width+7)>>3))&3;           // Padded to DWORD
437     for(y=height-1; y>=0; y--){
438       for(x=c=0,bit=0x80; x<width; x++){
439         if((data[y*width+x]&FXRGBA(0,0,0,255))==0) c|=bit;
440         bit>>=1;
441         if(bit==0){
442           store << c;
443           bit=0x80;
444           c=0;
445           }
446         }
447       store.save(padding,pad);
448       }
449     return true;
450     }
451   return false;
452   }
453 
454 
455 // 32 npp if alpha, 24 bpp otherwise
checkBPP(const FXColor * data,FXint width,FXint height)456 static FXushort checkBPP(const FXColor *data,FXint width,FXint height){
457   for(FXint i=0; i<width*height; ++i){
458     if((data[i]&FXRGBA(0,0,0,255))<FXRGBA(0,0,0,255)){ return 32; }
459     }
460   return 24;
461   }
462 
463 /*******************************************************************************/
464 
465 // Load BMP image from stream
fxloadBMP(FXStream & store,FXColor * & data,FXint & width,FXint & height)466 FXbool fxloadBMP(FXStream& store,FXColor*& data,FXint& width,FXint& height){
467   FXbool swap=store.swapBytes();
468   FXbool result=false;
469   FXint colors;
470   FXint format;
471   FXushort ss;
472 
473   // Null out
474   data=NULL;
475   width=0;
476   height=0;
477 
478   // Make little-endian
479   store.setBigEndian(false);
480 
481   // Get size and offset
482   BitmapFileHeader bfh;
483   store >> bfh.bfType;
484   store >> bfh.bfSize;
485   store >> bfh.bfReserved1;
486   store >> bfh.bfReserved2;
487   store >> bfh.bfOffBits;
488 
489   // Check signature
490   if(bfh.bfType==0x4d42){
491 
492     // Read bitmap info header
493     BitmapInfoHeader bmi;
494     store >> bmi.biSize;
495 
496     // Old OS/2 format header
497     if(bmi.biSize==OS2_OLD){
498       store >> ss; bmi.biWidth=ss;
499       store >> ss; bmi.biHeight=ss;
500       store >> bmi.biPlanes;
501       store >> bmi.biBitCount;
502       bmi.biCompression=BIH_RGB;
503       bmi.biSizeImage=(((bmi.biPlanes*bmi.biBitCount*bmi.biWidth)+31)>>5)*4*bmi.biHeight;
504       bmi.biXPelsPerMeter=0;
505       bmi.biYPelsPerMeter=0;
506       bmi.biClrUsed=0;
507       bmi.biClrImportant=0;
508       }
509 
510     // New Windows header
511     else{
512       store >> bmi.biWidth;
513       store >> bmi.biHeight;
514       store >> bmi.biPlanes;
515       store >> bmi.biBitCount;
516       store >> bmi.biCompression;
517       store >> bmi.biSizeImage;
518       store >> bmi.biXPelsPerMeter;
519       store >> bmi.biYPelsPerMeter;
520       store >> bmi.biClrUsed;
521       store >> bmi.biClrImportant;
522       store.position(bmi.biSize-WIN_NEW,FXFromCurrent);
523       }
524 
525     FXTRACE((100,"fxloadBMP: biSize=%d biWidth=%d biHeight=%d biPlanes=%d biBitCount=%d biCompression=%d biSizeImage=%u biClrUsed=%u biClrImportant=%u\n",bmi.biSize,bmi.biWidth,bmi.biHeight,bmi.biPlanes,bmi.biBitCount,bmi.biCompression,bmi.biSizeImage,bmi.biClrUsed,bmi.biClrImportant));
526 
527     // Check for sensible inputs
528     if(bmi.biPlanes==1 && 0<bmi.biWidth && 0<bmi.biHeight && bmi.biClrUsed<=256){
529 
530       // Width and height
531       width=bmi.biWidth;
532       height=FXABS(bmi.biHeight);
533       colors=bmi.biClrUsed?bmi.biClrUsed:1<<bmi.biBitCount;
534       format=(bmi.biSize==OS2_OLD||bmi.biSize==OS2_NEW)?3:4;
535 
536       // Load the bits
537       result=fxloadBMPBits(store,data,width,height,bmi.biBitCount,bmi.biCompression,colors,format);
538       }
539     }
540 
541   // Restore byte order
542   store.swapBytes(swap);
543   return result;
544   }
545 
546 /*******************************************************************************/
547 
548 // Load DIB image from stream
fxloadDIB(FXStream & store,FXColor * & data,FXint & width,FXint & height)549 FXbool fxloadDIB(FXStream& store,FXColor*& data,FXint& width,FXint& height){
550   FXbool swap=store.swapBytes();
551   FXbool result=false;
552   FXint colors;
553 
554   // Null out
555   data=NULL;
556   width=0;
557   height=0;
558 
559   // Make little-endian
560   store.setBigEndian(false);
561 
562   // Read bitmap info header
563   BitmapInfoHeader bmi;
564   store >> bmi.biSize;
565   store >> bmi.biWidth;
566   store >> bmi.biHeight;
567   store >> bmi.biPlanes;
568   store >> bmi.biBitCount;
569   store >> bmi.biCompression;
570   store >> bmi.biSizeImage;
571   store >> bmi.biXPelsPerMeter;
572   store >> bmi.biYPelsPerMeter;
573   store >> bmi.biClrUsed;
574   store >> bmi.biClrImportant;
575 
576   FXTRACE((100,"fxloadBMPStream: biSize=%d biWidth=%d biHeight=%d biPlanes=%d biBitCount=%d biCompression=%d biSizeImage=%u biClrUsed=%u biClrImportant=%u\n",bmi.biSize,bmi.biWidth,bmi.biHeight,bmi.biPlanes,bmi.biBitCount,bmi.biCompression,bmi.biSizeImage,bmi.biClrUsed,bmi.biClrImportant));
577 
578   // Check for sensible inputs
579   if(bmi.biPlanes==1 && 0<bmi.biWidth && 0<bmi.biHeight && bmi.biClrUsed<=256){
580 
581     // Width and height
582     width=bmi.biWidth;
583     height=FXABS(bmi.biHeight);
584     colors=bmi.biClrUsed?bmi.biClrUsed:1<<bmi.biBitCount;
585 
586     // Skip rest of header
587     store.position(bmi.biSize-sizeof(BitmapInfoHeader),FXFromCurrent);
588 
589     // Load the bits
590     result=fxloadBMPBits(store,data,width,height,bmi.biBitCount,bmi.biCompression,colors,4);
591     }
592 
593   // Restore byte order
594   store.swapBytes(swap);
595   return result;
596   }
597 
598 /*******************************************************************************/
599 
600 // Save BMP image to file stream
fxsaveBMP(FXStream & store,const FXColor * data,FXint width,FXint height)601 FXbool fxsaveBMP(FXStream& store,const FXColor *data,FXint width,FXint height){
602   FXbool result=false;
603 
604   // Must make sense
605   if(data && 0<width && 0<height){
606 
607     // Save byte order
608     FXbool swap=store.swapBytes();
609 
610     // Use alpha channel if image not opaque
611     FXushort bpp=checkBPP(data,width,height);
612 
613     // Make little-endian
614     store.setBigEndian(false);
615 
616     // BitmapFileHeader
617     BitmapFileHeader bfh={0x4d42,FXuint(14+WIN_NEW+height*(((width*bpp+31)>>5)<<2)),0,0,14+WIN_NEW};
618 
619     // Initialize bitmap info header
620     BitmapInfoHeader bmi={WIN_NEW,width,height,1,bpp,BIH_RGB,FXuint(height*(((width*bpp+31)>>5)<<2)),75*39,75*39,0,0};
621 
622     // BitmapFileHeader
623     store << bfh.bfType;        // Magic number
624     store << bfh.bfSize;        // File size
625     store << bfh.bfReserved1;   // bfReserved1
626     store << bfh.bfReserved2;   // bfReserved2
627     store << bfh.bfOffBits;     // bfOffBits
628 
629     // Bitmap Info Header
630     store << bmi.biSize;
631     store << bmi.biWidth;
632     store << bmi.biHeight;
633     store << bmi.biPlanes;
634     store << bmi.biBitCount;            // biBitCount (1,4,8,24, or 32)
635     store << bmi.biCompression;         // biCompression:  BIH_RGB, BIH_RLE8, BIH_RLE4, or BIH_BITFIELDS
636     store << bmi.biSizeImage;
637     store << bmi.biXPelsPerMeter;       // biXPelsPerMeter: (75dpi * 39" per meter)
638     store << bmi.biYPelsPerMeter;       // biYPelsPerMeter: (75dpi * 39" per meter)
639     store << bmi.biClrUsed;
640     store << bmi.biClrImportant;
641 
642     // Save pixels
643     result=fxsaveBMPBits(store,data,width,height,bpp);
644 
645     // Restore byte order
646     store.swapBytes(swap);
647     }
648   return result;
649   }
650 
651 /*******************************************************************************/
652 
653 // Save DIB image to stream
fxsaveDIB(FXStream & store,const FXColor * data,FXint width,FXint height)654 FXbool fxsaveDIB(FXStream& store,const FXColor *data,FXint width,FXint height){
655   FXbool result=false;
656 
657   // Must make sense
658   if(data && 0<width && 0<height){
659 
660     // Save byte order
661     FXbool swap=store.swapBytes();
662 
663     // Use alpha channel if image not opaque
664     FXushort bpp=checkBPP(data,width,height);
665 
666     // Make little-endian
667     store.setBigEndian(false);
668 
669     // Initialize bitmap info header
670     BitmapInfoHeader bmi={WIN_NEW,width,height,1,bpp,BIH_RGB,FXuint(height*(((width*bpp+31)>>5)<<2)),75*39,75*39,0,0};
671 
672     // Bitmap Info Header
673     store << bmi.biSize;
674     store << bmi.biWidth;
675     store << bmi.biHeight;
676     store << bmi.biPlanes;
677     store << bmi.biBitCount;            // biBitCount (1,4,8,24, or 32)
678     store << bmi.biCompression;         // biCompression:  BIH_RGB, BIH_RLE8, BIH_RLE4, or BIH_BITFIELDS
679     store << bmi.biSizeImage;           // Image size in bytes
680     store << bmi.biXPelsPerMeter;       // biXPelsPerMeter: (75dpi * 39" per meter)
681     store << bmi.biYPelsPerMeter;       // biYPelsPerMeter: (75dpi * 39" per meter)
682     store << bmi.biClrUsed;
683     store << bmi.biClrImportant;
684 
685     // Save pixels
686     result=fxsaveBMPBits(store,data,width,height,bpp);
687 
688     // Restore byte order
689     store.swapBytes(swap);
690     }
691   return result;
692   }
693 
694 /*******************************************************************************/
695 
696 // Load ICO image from stream
fxloadICO(FXStream & store,FXColor * & data,FXint & width,FXint & height,FXint & xspot,FXint & yspot)697 FXbool fxloadICO(FXStream& store,FXColor*& data,FXint& width,FXint& height,FXint& xspot,FXint& yspot){
698   FXbool swap=store.swapBytes();
699   FXbool result=false;
700   FXint colors;
701 
702   // Null out
703   data=NULL;
704   width=0;
705   height=0;
706   xspot=-1;
707   yspot=-1;
708 
709   // Make little-endian
710   store.setBigEndian(false);
711 
712   // Icon Directory Header
713   IconDirectory icd;
714   store >> icd.idReserved;      // Must be zero
715   store >> icd.idType;          // Must be 1 (icon) or 2 (cursor)
716   store >> icd.idCount;         // Only one icon
717 
718   // Validity of icon directory header
719   if(icd.idReserved==0 && 0<icd.idCount && (icd.idType==IDH_ICO || icd.idType==IDH_CUR)){
720 
721     // Icon Directory Entry
722     IconDirectoryEntry ice;
723     store >> ice.bWidth;
724     store >> ice.bHeight;
725     store >> ice.bColorCount;     // 0 for > 8bit/pixel
726     store >> ice.bReserved;       // 0
727     store >> ice.wXHotspot;       // X hotspot if cursor, #planes if icon
728     store >> ice.wYHotspot;       // Y hotspot if cursor, #bits/pixel if icon
729     store >> ice.dwBytesInRes;    // Total number of bytes in images (including palette data)
730     store >> ice.dwImageOffset;   // Location of image from the beginning of file
731 
732     // Skip to bitmap info header
733     store.position(ice.dwImageOffset-22,FXFromCurrent);
734 
735     // Initialize bitmap info header
736     BitmapInfoHeader bmi;
737     store >> bmi.biSize;
738     store >> bmi.biWidth;
739     store >> bmi.biHeight;
740     store >> bmi.biPlanes;
741     store >> bmi.biBitCount;
742     store >> bmi.biCompression;
743     store >> bmi.biSizeImage;
744     store >> bmi.biXPelsPerMeter;
745     store >> bmi.biYPelsPerMeter;
746     store >> bmi.biClrUsed;
747     store >> bmi.biClrImportant;
748 
749     // Skip rest of header
750     store.position(bmi.biSize-WIN_NEW,FXFromCurrent);
751 
752     FXTRACE((100,"fxloadICO: idCount=%d idType=%d wXHotspot=%d wYHotspot=%d\n",icd.idCount,icd.idType,ice.wXHotspot,ice.wYHotspot));
753     FXTRACE((100,"fxloadICO: biSize=%d biWidth=%d biHeight=%d biPlanes=%d biBitCount=%d biCompression=%d biSizeImage=%u biClrUsed=%u biClrImportant=%u\n",bmi.biSize,bmi.biWidth,bmi.biHeight,bmi.biPlanes,bmi.biBitCount,bmi.biCompression,bmi.biSizeImage,bmi.biClrUsed,bmi.biClrImportant));
754 
755     // Check for sensible inputs
756     if(bmi.biPlanes==1 && 0<bmi.biWidth && 0<bmi.biHeight && bmi.biClrUsed<=256){
757 
758       // Width and height
759       width=bmi.biWidth;
760       height=FXABS(bmi.biHeight)/2;
761       colors=bmi.biClrUsed?bmi.biClrUsed:1<<bmi.biBitCount;
762 
763       // Copy hotspot location if cursor
764       if(icd.idType==IDH_CUR){
765         xspot=ice.wXHotspot;
766         yspot=ice.wYHotspot;
767         }
768 
769       // Load the bits
770       result=fxloadICOBits(store,data,width,height,bmi.biBitCount,bmi.biCompression,colors,4);
771       }
772     }
773 
774   // Restore byte order
775   store.swapBytes(swap);
776   return result;
777   }
778 
779 
780 // Load ICO Image from stream
fxloadICOStream(FXStream & store,FXColor * & data,FXint & width,FXint & height)781 FXbool fxloadICOStream(FXStream& store,FXColor*& data,FXint& width,FXint& height){
782   FXbool swap=store.swapBytes();
783   FXbool result=false;
784   FXint colors;
785 
786   // Null out
787   data=NULL;
788   width=0;
789   height=0;
790 
791   // Bitmaps are little-endian
792   store.setBigEndian(false);
793 
794   // Read bitmap info header
795   BitmapInfoHeader bmi;
796   store >> bmi.biSize;
797   store >> bmi.biWidth;
798   store >> bmi.biHeight;
799   store >> bmi.biPlanes;
800   store >> bmi.biBitCount;
801   store >> bmi.biCompression;
802   store >> bmi.biSizeImage;
803   store >> bmi.biXPelsPerMeter;
804   store >> bmi.biYPelsPerMeter;
805   store >> bmi.biClrUsed;
806   store >> bmi.biClrImportant;
807 
808   // Skip rest of header
809   store.position(bmi.biSize-WIN_NEW,FXFromCurrent);
810 
811   FXTRACE((100,"fxloadICOStream: biSize=%d biWidth=%d biHeight=%d biBitCount=%d biCompression=%d biSizeImage=%d biClrUsed=%d\n",bmi.biSize,bmi.biWidth,bmi.biHeight,bmi.biBitCount,bmi.biCompression,bmi.biSizeImage,bmi.biClrUsed));
812 
813   // Check for sensible inputs
814   if(bmi.biPlanes==1 && 0<bmi.biWidth && 0<bmi.biHeight && bmi.biClrUsed<=256){
815 
816     // Width and height
817     width=bmi.biWidth;
818     height=FXABS(bmi.biHeight)/2;         // Topsy turvy possibility; adjust height also
819     colors=bmi.biClrUsed?bmi.biClrUsed:1<<bmi.biBitCount;
820 
821     // Load the bits
822     result=fxloadICOBits(store,data,width,height,bmi.biBitCount,bmi.biCompression,colors,4);
823     }
824 
825   // Restore byte order
826   store.swapBytes(swap);
827   return result;
828   }
829 
830 /*******************************************************************************/
831 
832 // Save a ICO file to a stream
fxsaveICO(FXStream & store,const FXColor * data,FXint width,FXint height,FXint xspot,FXint yspot)833 FXbool fxsaveICO(FXStream& store,const FXColor *data,FXint width,FXint height,FXint xspot,FXint yspot){
834   FXbool result=false;
835 
836   // Must make sense
837   if(data && 0<width && 0<height && width<256 && height<256){
838 
839     // Save byte order
840     FXbool swap=store.swapBytes();
841 
842     // Use alpha channel if image not opaque
843     FXushort bpp=checkBPP(data,width,height);
844 
845     // Bitmaps are little-endian
846     store.setBigEndian(false);
847 
848     // Initialize icon directory header
849     IconDirectory icd={0,IDH_CUR,1};
850 
851     // Initialize icon directory entry
852     IconDirectoryEntry ice={(FXuchar)width,(FXuchar)height,0,0,(FXushort)xspot,(FXushort)yspot,FXuint(WIN_NEW+height*((((width*bpp+31)>>5)<<2)+(((width+31)>>5)<<2))),22};
853 
854     // Initialize bitmap info header
855     BitmapInfoHeader bmi={WIN_NEW,width,height*2,1,bpp,BIH_RGB,FXuint(height*(((width*bpp+31)>>5)<<2)),75*39,75*39,0,0};
856 
857     // Save as ico if no hotspot
858     if(xspot<0 || yspot<0){
859       icd.idType=IDH_ICO;
860       ice.wXHotspot=1;
861       ice.wYHotspot=bpp;
862       }
863 
864     // Icon Directory Header
865     store << icd.idReserved;      // Must be zero
866     store << icd.idType;          // Must be 1 (icon) or 2 (cursor)
867     store << icd.idCount;         // Only one icon
868 
869     // Icon Directory Entry
870     store << ice.bWidth;
871     store << ice.bHeight;
872     store << ice.bColorCount;     // 0 for > 8bit/pixel
873     store << ice.bReserved;       // 0
874     store << ice.wXHotspot;       // X hotspot if cursor, #planes if icon
875     store << ice.wYHotspot;       // Y hotspot if cursor, #bits/pixel if icon
876     store << ice.dwBytesInRes;    // Total number of bytes in images (including palette data)
877     store << ice.dwImageOffset;   // Location of image from the beginning of file
878 
879     // Bitmap Info Header
880     store << bmi.biSize;
881     store << bmi.biWidth;
882     store << bmi.biHeight;
883     store << bmi.biPlanes;
884     store << bmi.biBitCount;
885     store << bmi.biCompression;
886     store << bmi.biSizeImage;
887     store << bmi.biXPelsPerMeter;
888     store << bmi.biYPelsPerMeter;
889     store << bmi.biClrUsed;
890     store << bmi.biClrImportant;
891 
892     // Save pixels
893     result=fxsaveICOBits(store,data,width,height,bpp);
894 
895     // Restore byte order
896     store.swapBytes(swap);
897     }
898   return result;
899   }
900 
901 }
902