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