1 /********************************************************************************
2 *                                                                               *
3 *                             B i t m a p    O b j e c 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 "FXMutex.h"
29 #include "FXException.h"
30 #include "FXElement.h"
31 #include "FXStream.h"
32 #include "FXString.h"
33 #include "FXSize.h"
34 #include "FXPoint.h"
35 #include "FXRectangle.h"
36 #include "FXStringDictionary.h"
37 #include "FXSettings.h"
38 #include "FXRegistry.h"
39 #include "FXVisual.h"
40 #include "FXEvent.h"
41 #include "FXWindow.h"
42 #include "FXDCWindow.h"
43 #include "FXApp.h"
44 #include "FXBitmap.h"
45 
46 
47 /*
48   Note:
49   - Try eliminate temp copy:- slap pixels into XImage directly, if possible...
50   - Perhaps enforce system-native padding necessary for the above.
51   - Our bitmap data is 01234567, i.e. LS-BIT first; byte order ditto.
52   - Issue: should FXBitmap return the DC for drawing onto the X-Server resident
53     pixmap, or the client-side image bits?
54 
55     My idea is it should be the latter:
56 
57       - Allows even richer set of drawing primitives, as everything is
58         drawn in software.
59       - Very useful to generate off-screen renderings, e.g. during printing.
60       - Allows for building and running true-color drawing programs on
61         low-end graphics hardware.
62       - The only drawback I can see is it will be a fairly large implementation
63         effort...
64 
65   - The scale, mirror, rotate and crop API's have been contributed by Marc Cartright,
66     <macartright@hotmail.com>.
67   - The XBM format maps 1 to black instead of white; perhaps this should
68     be reversed.
69 */
70 
71 
72 // Changable bitmap options
73 #define BITMAP_MASK   (BITMAP_KEEP|BITMAP_SHMI|BITMAP_SHMP)
74 
75 
76 using namespace FX;
77 
78 /*******************************************************************************/
79 
80 namespace FX {
81 
82 
83 // Object implementation
84 FXIMPLEMENT(FXBitmap,FXDrawable,NULL,0)
85 
86 
87 // For deserialization
FXBitmap()88 FXBitmap::FXBitmap(){
89   data=NULL;
90   bytewidth=0;
91   options=0;
92   }
93 
94 
95 // Initialize
FXBitmap(FXApp * a,const void * pix,FXuint opts,FXint w,FXint h)96 FXBitmap::FXBitmap(FXApp* a,const void *pix,FXuint opts,FXint w,FXint h):FXDrawable(a,w,h){
97   FXTRACE((100,"FXBitmap::FXBitmap %p\n",this));
98   FXASSERT((opts&~(BITMAP_OWNED|BITMAP_MASK))==0);
99   visual=getApp()->getMonoVisual();
100   data=(FXuchar*)pix;
101   bytewidth=(width+7)>>3;
102   options=opts;
103   if(!data && (options&BITMAP_OWNED)){
104     if(!callocElms(data,height*bytewidth)){ throw FXMemoryException("unable to construct bitmap"); }
105     }
106   }
107 
108 
109 // Create bitmap
create()110 void FXBitmap::create(){
111   if(!xid){
112     if(getApp()->isInitialized()){
113       FXTRACE((100,"%s::create %p\n",getClassName(),this));
114 
115       // Initialize visual
116       visual->create();
117 
118 #if defined(WIN32)
119       xid=CreateBitmap(FXMAX(width,1),FXMAX(height,1),1,1,NULL);
120 #else
121       xid=XCreatePixmap((Display*)getApp()->getDisplay(),XDefaultRootWindow((Display*)getApp()->getDisplay()),FXMAX(width,1),FXMAX(height,1),1);
122 #endif
123 
124       // Were we successful?
125       if(!xid){ throw FXImageException("unable to create bitmap"); }
126 
127       // Render pixels
128       render();
129 
130       // If we're not keeping the pixel buffer, release it
131       if(!(options&BITMAP_KEEP)) release();
132       }
133     }
134   }
135 
136 
137 // Release the client-side buffer, free it if it was owned.
release()138 void FXBitmap::release(){
139   if(options&BITMAP_OWNED){
140     options&=~BITMAP_OWNED;
141     freeElms(data);
142     }
143   data=NULL;
144   }
145 
146 
147 // Detach bitmap
detach()148 void FXBitmap::detach(){
149   visual->detach();
150   if(xid){
151     FXTRACE((100,"%s::detach %p\n",getClassName(),this));
152     xid=0;
153     }
154   }
155 
156 
157 // Destroy bitmap
destroy()158 void FXBitmap::destroy(){
159   if(xid){
160     if(getApp()->isInitialized()){
161       FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
162 #if defined(WIN32)
163       DeleteObject(xid);
164 #else
165       XFreePixmap((Display*)getApp()->getDisplay(),xid);
166 #endif
167       }
168     xid=0;
169     }
170   }
171 
172 
173 #if defined(WIN32)      // WINDOWS
174 
175 
176 struct BITMAPINFO256 {
177   BITMAPINFOHEADER bmiHeader;
178   RGBQUAD          bmiColors[256];
179   };
180 
181 
182 // Restore client-side pixel buffer from bitmap
restore()183 void FXBitmap::restore(){
184   if(xid){
185 
186     FXTRACE((100,"%s::restore image %p\n",getClassName(),this));
187 
188     // Check for legal size
189     if(width<1 || height<1){ fxerror("%s::restore: illegal image size %dx%d.\n",getClassName(),width,height); }
190 
191     // Make array for data if needed
192     if(!data){
193       if(!callocElms(data,height*bytewidth)){ throw FXMemoryException("unable to restore image"); }
194       options|=BITMAP_OWNED;
195       }
196 
197     // Got local buffer to receive into
198     if(data){
199       FXuchar *pixels,*p,*q;;
200       FXint x,y;
201 
202       // Bytes per line, rounded to nearest DWORD
203       FXint bytes_per_line=((width+31)&~31)>>3;
204 
205       // Set up the bitmap info
206       BITMAPINFO256 bmi;
207       bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
208       bmi.bmiHeader.biWidth=width;
209       bmi.bmiHeader.biHeight=-height;   // Negative heights means upside down!
210       bmi.bmiHeader.biPlanes=1;
211       bmi.bmiHeader.biBitCount=1;
212       bmi.bmiHeader.biCompression=BI_RGB;
213       bmi.bmiHeader.biSizeImage=0;
214       bmi.bmiHeader.biXPelsPerMeter=0;
215       bmi.bmiHeader.biYPelsPerMeter=0;
216       bmi.bmiHeader.biClrUsed=0;
217       bmi.bmiHeader.biClrImportant=0;
218       bmi.bmiColors[0].rgbBlue=0;
219       bmi.bmiColors[0].rgbGreen=0;
220       bmi.bmiColors[0].rgbRed=0;
221       bmi.bmiColors[0].rgbReserved=0;
222       bmi.bmiColors[1].rgbBlue=255;
223       bmi.bmiColors[1].rgbGreen=255;
224       bmi.bmiColors[1].rgbRed=255;
225       bmi.bmiColors[1].rgbReserved=0;
226 
227       // DIB format pads to multiples of 4 bytes...
228       if(!allocElms(pixels,height*bytes_per_line)){ throw FXImageException("unable to restore image"); }
229 
230       // Make device context
231       HDC hdcmem=::CreateCompatibleDC(NULL);
232       if(!GetDIBits(hdcmem,(HBITMAP)xid,0,height,pixels,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)){
233         throw FXImageException("unable to restore image");
234         }
235 
236       // Fill our own data from pixels
237       for(y=0,p=pixels,q=data; y<height; y++){
238         for(x=0; x<bytewidth; x++){
239           q[x]=~reverse8(p[x]);
240           }
241         q+=bytewidth;
242         p+=bytes_per_line;
243         }
244 
245       // Clean up
246       ::DeleteDC(hdcmem);
247       freeElms(pixels);
248       }
249     }
250   }
251 
252 
253 // Render into pixmap
render()254 void FXBitmap::render(){
255   if(xid){
256 
257     FXTRACE((100,"%s::render bitmap %p\n",getClassName(),this));
258 
259     // Fill with pixels if there is data
260     if(data && 0<width && 0<height){
261       FXuchar *pixels,*p,*q;
262       FXint x,y;
263 
264       // Bytes per line, rounded to nearest DWORD
265       FXint bytes_per_line=((width+31)&~31)>>3;
266 
267       // Set up the bitmap info
268       BITMAPINFO256 bmi;
269       bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
270       bmi.bmiHeader.biWidth=width;
271       bmi.bmiHeader.biHeight=-height;   // Negative heights means upside down!
272       bmi.bmiHeader.biPlanes=1;
273       bmi.bmiHeader.biBitCount=1;
274       bmi.bmiHeader.biCompression=0;
275       bmi.bmiHeader.biSizeImage=0;
276       bmi.bmiHeader.biXPelsPerMeter=0;
277       bmi.bmiHeader.biYPelsPerMeter=0;
278       bmi.bmiHeader.biClrUsed=0;
279       bmi.bmiHeader.biClrImportant=0;
280       bmi.bmiColors[0].rgbBlue=0;
281       bmi.bmiColors[0].rgbGreen=0;
282       bmi.bmiColors[0].rgbRed=0;
283       bmi.bmiColors[0].rgbReserved=0;
284       bmi.bmiColors[1].rgbBlue=255;
285       bmi.bmiColors[1].rgbGreen=255;
286       bmi.bmiColors[1].rgbRed=255;
287       bmi.bmiColors[1].rgbReserved=0;
288 
289       // Fill temp array
290       if(!callocElms(pixels,height*bytes_per_line)){ throw FXMemoryException("unable to render bitmap"); }
291 
292       // Fill pixels from our own data
293       for(y=0,p=pixels,q=data; y<height; y++){
294         for(x=0; x<bytewidth; x++){
295           p[x]=~reverse8(q[x]);
296           }
297         q+=bytewidth;
298         p+=bytes_per_line;
299         }
300 
301       // Get memory device context
302       HDC hdcmem=::CreateCompatibleDC(NULL);
303       if(!SetDIBits(hdcmem,(HBITMAP)xid,0,height,pixels,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)){
304         throw FXImageException("unable to render bitmap");
305         }
306 
307       // Push to GDI
308       ::GdiFlush();
309 
310       // Clean up
311       ::DeleteDC(hdcmem);
312       freeElms(pixels);
313       }
314     }
315   }
316 
317 
318 #else                   // X11
319 
320 
321 // Restore client-side pixel buffer from bitmap
restore()322 void FXBitmap::restore(){
323   if(xid){
324     XImage *xim=NULL;
325     FXint size,x,y;
326 
327     FXTRACE((100,"%s::restore bitmap %p\n",getClassName(),this));
328 
329     // Check for legal size
330     if(width<1 || height<1){ fxerror("%s::restore: illegal bitmap size %dx%d.\n",getClassName(),width,height); }
331 
332     // Make array for data if needed
333     if(!data){
334       size=bytewidth*height;
335       if(!callocElms(data,size)){ throw FXMemoryException("unable to restore bitmap"); }
336       options|=BITMAP_OWNED;
337       }
338 
339     // Got local buffer to receive into
340     if(data){
341       xim=XGetImage((Display*)getApp()->getDisplay(),xid,0,0,width,height,1,XYPixmap);
342       if(!xim){ throw FXImageException("unable to restore image"); }
343 
344       // Should have succeeded
345       FXASSERT(xim);
346 
347       FXTRACE((150,"bm width = %d\n",xim->width));
348       FXTRACE((150,"bm height = %d\n",xim->height));
349       FXTRACE((150,"bm format = %s\n",xim->format==XYBitmap?"XYBitmap":xim->format==XYPixmap?"XYPixmap":"ZPixmap"));
350       FXTRACE((150,"bm byte_order = %s\n",(xim->byte_order==MSBFirst)?"MSBFirst":"LSBFirst"));
351       FXTRACE((150,"bm bitmap_unit = %d\n",xim->bitmap_unit));
352       FXTRACE((150,"bm bitmap_bit_order = %s\n",(xim->bitmap_bit_order==MSBFirst)?"MSBFirst":"LSBFirst"));
353       FXTRACE((150,"bm bitmap_pad = %d\n",xim->bitmap_pad));
354       FXTRACE((150,"bm bitmap_unit = %d\n",xim->bitmap_unit));
355       FXTRACE((150,"bm depth = %d\n",xim->depth));
356       FXTRACE((150,"bm bytes_per_line = %d\n",xim->bytes_per_line));
357       FXTRACE((150,"bm bits_per_pixel = %d\n",xim->bits_per_pixel));
358 
359       // Grab pixels from image
360       for(y=0; y<height; y++){
361         for(x=0; x<width; x++){
362           if(XGetPixel(xim,x,y)) data[y*bytewidth+(x>>3)]|=1<<(x&7);
363           }
364         }
365 
366       // Destroy image
367       XDestroyImage(xim);
368       }
369     }
370   }
371 
372 
373 // Render into pixmap
render()374 void FXBitmap::render(){
375   if(xid){
376     XImage *xim=NULL;
377     int size;
378     FXuchar *pix;
379     int i;
380     XGCValues values;
381     GC gc;
382 
383     FXTRACE((100,"%s::render bitmap %p\n",getClassName(),this));
384 
385     // Fill with pixels if there is data
386     if(data && 0<width && 0<height){
387 
388       // Make GC
389       values.foreground=0xffffffff;
390       values.background=0;
391       gc=XCreateGC((Display*)getApp()->getDisplay(),xid,GCForeground|GCBackground,&values);
392 
393       // Create image to transfer pixels
394       xim=XCreateImage((Display*)getApp()->getDisplay(),(Visual*)visual->visual,1,XYBitmap,0,NULL,width,height,8,(width+7)>>3);
395       if(!xim){ throw FXImageException("unable to render bitmap"); }
396 
397       // Try create temp pixel store
398       if(!allocElms(xim->data,xim->bytes_per_line*height)){ throw FXMemoryException("unable to render bitmap"); }
399 
400       FXTRACE((150,"bm width = %d\n",xim->width));
401       FXTRACE((150,"bm height = %d\n",xim->height));
402       FXTRACE((150,"bm format = %s\n",xim->format==XYBitmap?"XYBitmap":xim->format==XYPixmap?"XYPixmap":"ZPixmap"));
403       FXTRACE((150,"bm byte_order = %s\n",(xim->byte_order==MSBFirst)?"MSBFirst":"LSBFirst"));
404       FXTRACE((150,"bm bitmap_unit = %d\n",xim->bitmap_unit));
405       FXTRACE((150,"bm bitmap_bit_order = %s\n",(xim->bitmap_bit_order==MSBFirst)?"MSBFirst":"LSBFirst"));
406       FXTRACE((150,"bm bitmap_pad = %d\n",xim->bitmap_pad));
407       FXTRACE((150,"bm bitmap_unit = %d\n",xim->bitmap_unit));
408       FXTRACE((150,"bm depth = %d\n",xim->depth));
409       FXTRACE((150,"bm bytes_per_line = %d\n",xim->bytes_per_line));
410       FXTRACE((150,"bm bits_per_pixel = %d\n",xim->bits_per_pixel));
411 
412       // Render bits into server-formatted bitmap
413       size=xim->bytes_per_line*height;
414       pix=(FXuchar*)xim->data;
415 
416       // Most significant bit first
417       if(xim->bitmap_bit_order==MSBFirst){
418         for(i=0; i<size; i++) pix[i]=reverse8(data[i]);
419         }
420 
421       // Least significant bit first
422       else{
423         memcpy(pix,data,size);
424         }
425 
426       // Blast the image
427       XPutImage((Display*)getApp()->getDisplay(),xid,gc,xim,0,0,0,0,width,height);
428       freeElms(xim->data);
429       XDestroyImage(xim);
430       XFreeGC((Display*)getApp()->getDisplay(),gc);
431       }
432     }
433   }
434 
435 #endif
436 
437 
438 // Resize bitmap to the specified width and height; the contents become undefined
resize(FXint w,FXint h)439 void FXBitmap::resize(FXint w,FXint h){
440   FXint bw;
441   if(w<1) w=1;
442   if(h<1) h=1;
443   FXTRACE((100,"%s::resize(%d,%d)\n",getClassName(),w,h));
444   bw=(w+7)>>3;
445   if(xid){
446 
447 #if defined(WIN32)
448 
449     // Delete old bitmap
450     DeleteObject(xid);
451 
452     // Create a bitmap compatible with current display
453     xid=CreateBitmap(w,h,1,1,NULL);
454     if(!xid){ throw FXImageException("unable to resize bitmap"); }
455 
456 #else
457 
458     // Free old pixmap
459     XFreePixmap((Display*)getApp()->getDisplay(),xid);
460 
461     // Make new pixmap
462     xid=XCreatePixmap((Display*)getApp()->getDisplay(),XDefaultRootWindow((Display*)getApp()->getDisplay()),w,h,1);
463     if(!xid){ throw FXImageException("unable to resize bitmap"); }
464 
465 #endif
466 
467     }
468 
469   // Resize data array; only do the work if the new
470   // array is a different size as measured in bytes!
471   if(data){
472     if(!(options&BITMAP_OWNED)){        // Need to own array
473       if(!allocElms(data,h*bw)){ throw FXMemoryException("unable to resize bitmap"); }
474       options|=BITMAP_OWNED;
475       }
476     else if(h*bw!=height*bytewidth){
477       if(!resizeElms(data,h*bw)){ throw FXMemoryException("unable to resize bitmap"); }
478       }
479     }
480 
481   // Remember new size
482   bytewidth=bw;
483   width=w;
484   height=h;
485   }
486 
487 
488 // Fill bitmap with uniform value
fill(FXbool color)489 void FXBitmap::fill(FXbool color){
490   if(data){
491     memset(data,0-color,height*bytewidth);
492     }
493   }
494 
495 
496 // Rescale pixels to the specified width and height; just nearest
497 // neighbor; there ain't no such thing as anti-aliasing in bitmaps!
scale(FXint w,FXint h)498 void FXBitmap::scale(FXint w,FXint h){
499   if(w<1) w=1;
500   if(h<1) h=1;
501   FXTRACE((100,"%s::scale(%d,%d)\n",getClassName(),w,h));
502   if(w!=width || h!=height){
503     if(data){
504       FXuchar *q,*p,bits;
505       FXint xs=(width<<16)/w;
506       FXint ys=(height<<16)/h;
507       FXint bw=bytewidth;
508       FXint i,j,x,y,xx;
509       FXuchar *interim;
510 
511       // Copy to old buffer
512       if(!dupElms(interim,data,height*bytewidth)){ throw FXMemoryException("unable to scale bitmap"); }
513 
514       // Resize the pixmap and target buffer
515       resize(w,h);
516 
517       // Scale the bitmap
518       i=0;
519       y=ys>>1;
520       p=data;
521       do{
522         j=0;
523         x=xs>>1;
524         q=interim+(y>>16)*bw;
525         bits=0;
526         do{
527           xx=x>>16;
528           bits|=((q[xx>>3]>>(xx&7))&1)<<(j&7);
529           if((j&7)==7){ *p++=bits; bits=0; }
530           x+=xs;
531           }
532         while(++j<w);
533         if(j&7){ *p++=bits; }
534         y+=ys;
535         }
536       while(++i<h);
537 
538       // Free interim buffer
539       freeElms(interim);
540       render();
541       }
542     else{
543       resize(w,h);
544       }
545     }
546   }
547 
548 
549 
550 // Mirror bitmap horizontally and/or vertically
mirror(FXbool horizontal,FXbool vertical)551 void FXBitmap::mirror(FXbool horizontal,FXbool vertical){
552   FXTRACE((100,"%s::mirror(%d,%d)\n",getClassName(),horizontal,vertical));
553   if(horizontal || vertical){
554     if(data){
555       FXuchar *paa,*pa,*pbb,*pb;
556       FXint sa=(8-width)&7;
557       FXint sb=8-sa;
558       FXuint t;
559       FXuchar line[4096];               // Maximum width is 32768/8=4096 bytes
560       if(vertical && height>1){         // Mirror vertically
561         paa=data;
562         pbb=data+bytewidth*(height-1);
563         do{
564           pa=paa; paa+=bytewidth;
565           pb=pbb; pbb-=bytewidth;
566           do{
567             t=*pa; *pa++=*pb; *pb++=t;
568             }
569           while(pa<paa);
570           }
571         while(paa<pbb);
572         }
573       if(horizontal && width>1){        // Mirror horizontally
574         paa=data;
575         pbb=data+bytewidth*height;
576         do{
577           pa=paa;
578           pb=line+bytewidth;
579           do{
580             *--pb=*paa++;               // Gnarly!
581             }
582           while(line<pb);
583           do{
584             t=*pb++ << sa;
585             t|=*pb >> sb;
586             *pa++=reverse8(t);
587             }
588           while(pa<paa);
589           }
590         while(paa<pbb);
591         }
592       render();
593       }
594     }
595   }
596 
597 
598 // Rotate bitmap by degrees ccw
rotate(FXint degrees)599 void FXBitmap::rotate(FXint degrees){
600   FXTRACE((100,"%s::rotate(%d)\n",getClassName(),degrees));
601   degrees=(degrees+360)%360;
602   if(degrees!=0 && width>1 && height>1){
603     if(data){
604       FXuchar *p,*q,bits;
605       FXint bw=bytewidth;
606       FXint i,j,x;
607       FXuchar *olddata;
608       if(!dupElms(olddata,data,bytewidth*height)){ throw FXMemoryException("unable to rotate bitmap"); }
609       switch(degrees){
610         case 90:
611           resize(height,width);
612           i=height-1;
613           p=data;
614           do{
615             j=0;
616             q=olddata+(i>>3);
617             bits=0;
618             do{
619               bits|=((q[0]>>(i&7))&1)<<(j&7);
620               if((j&7)==7){ *p++=bits; bits=0; }
621               q+=bw;
622               }
623             while(++j<width);
624             if(j&7){ *p++=bits; }
625             }
626           while(--i>=0);
627           break;
628         case 180:               // FIXME works but not as fast as it could be
629           i=height-1;
630           p=data;
631           q=olddata+(height-1)*bw;
632           do{
633             j=0;
634             bits=0;
635             x=width-1;
636             do{
637               bits|=((q[x>>3]>>(x&7))&1)<<(j&7);
638               if((j&7)==7){ *p++=bits; bits=0; }
639               x--;
640               }
641             while(++j<width);
642             if(j&7){ *p++=bits; }
643             q-=bw;
644             }
645           while(--i>=0);
646           break;
647         case 270:
648           resize(height,width);
649           i=0;
650           p=data;
651           do{
652             j=0;
653             q=olddata+(i>>3)+(width-1)*bw;
654             bits=0;
655             do{
656               bits|=((q[0]>>(i&7))&1)<<(j&7);
657               if((j&7)==7){ *p++=bits; bits=0; }
658               q-=bw;
659               }
660             while(++j<width);
661             if(j&7){ *p++=bits; }
662             }
663           while(++i<height);
664           break;
665         default:
666           fxwarning("%s::rotate: rotation by %d degrees not implemented.\n",getClassName(),degrees);
667           break;
668         }
669       freeElms(olddata);
670       render();
671       }
672     else{
673       switch(degrees){
674         case 90:
675           resize(height,width);
676           break;
677         case 180:
678           resize(width,height);
679           break;
680         case 270:
681           resize(height,width);
682           break;
683         default:
684           fxwarning("%s::rotate: rotation by %d degrees not implemented.\n",getClassName(),degrees);
685           break;
686         }
687       }
688     }
689   }
690 
691 
692 // Crop bitmap to given rectangle
crop(FXint x,FXint y,FXint w,FXint h,FXbool color)693 void FXBitmap::crop(FXint x,FXint y,FXint w,FXint h,FXbool color){
694   if(w<1) w=1;
695   if(h<1) h=1;
696   if(x>=width || y>=height || x+w<=0 || y+h<=0){ fxerror("%s::crop: bad arguments.\n",getClassName()); }
697   FXTRACE((100,"%s::crop(%d,%d,%d,%d)\n",getClassName(),x,y,w,h));
698   if(data){
699     FXuchar *pnn,*poo,*yyy,*pn,*po,*xx;
700     FXint oldbw=bytewidth;
701     FXint newbw=(w+7)>>3;
702     FXint cpybw;
703     FXint ow=width;
704     FXint oh=height;
705     FXint nw=w;
706     FXint nh=h;
707     FXint cw;
708     FXint ch;
709     FXint sh;
710     FXuint t;
711     FXuchar *olddata;
712     if(!allocElms(olddata,oh*bytewidth+1)){ throw FXMemoryException("unable to crop bitmap"); }
713     memcpy(olddata,data,oh*bytewidth);
714     resize(w,h);
715     pnn=data;
716     yyy=data+newbw*nh;
717     do{
718       *pnn++=0-color;           // 1 -> 0xff, 0 -> 0xff
719       }
720     while(pnn<yyy);
721     if(x<0){                    // x < 0
722       cw=FXMIN(ow,x+nw);
723       if(y<0){                  // y < 0
724         pnn=data-newbw*y;
725         poo=olddata;
726         ch=FXMIN(oh,y+nh);
727         }
728       else{                     // y >= 0
729         pnn=data;
730         poo=olddata+oldbw*y;
731         ch=FXMIN(oh,y+nh)-y;
732         }
733       pnn+=(-x)>>3;
734       sh=8-((-x)&7);
735       FXASSERT(cw>0);
736       FXASSERT(ch>0);
737       yyy=pnn+newbw*ch;
738       cpybw=((cw-x+7)>>3)-((-x)>>3);
739       //FXTRACE((1,"ow=%d oh=%d nw=%d nh=%d cw=%d ch=%d sh=%d cpybw=%d\n",ow,oh,nw,nh,cw,ch,sh,cpybw));
740       do{
741         pn=pnn;
742         po=poo;
743         xx=pnn+cpybw;
744         t=(0-color)&0xff;
745         do{
746           t|=(*po++)<<8;
747           *pn++=t>>sh;
748           t>>=8;
749           }
750         while(pn<xx);
751         if(color){              // A bit ugly but it'll have to do for now...
752           *(pn-1)|=0xff<<((cw-x)&7);
753           }
754         else{
755           *(pn-1)&=~(0xff<<((cw-x)&7));
756           }
757         pnn+=newbw;
758         poo+=oldbw;
759         }
760       while(pnn<yyy);
761       }
762     else{                       // x >= 0
763       cw=FXMIN(ow,x+nw)-x;
764       if(y<0){                  // y < 0
765         pnn=data-newbw*y;
766         poo=olddata;
767         ch=FXMIN(oh,y+nh);
768         }
769       else{                     // y >= 0
770         pnn=data;
771         poo=olddata+oldbw*y;
772         ch=FXMIN(oh,y+nh)-y;
773         }
774       poo+=x>>3;
775       sh=x&7;
776       FXASSERT(cw>0);
777       FXASSERT(ch>0);
778       yyy=pnn+newbw*ch;
779       cpybw=(cw+7)>>3;
780       do{
781         pn=pnn;
782         po=poo;
783         xx=pnn+cpybw;
784         do{
785           t=*po++;
786           t|=*po<<8;
787           *pn++=t>>sh;
788           }
789         while(pn<xx);
790         pnn+=newbw;
791         poo+=oldbw;
792         }
793       while(pnn<yyy);
794       }
795     freeElms(olddata);
796     render();
797     }
798   else{
799     resize(w,h);
800     }
801   }
802 
803 
804 
805 #if defined(WIN32)
806 
807 // Get the image's device context
GetDC() const808 FXID FXBitmap::GetDC() const {
809   HDC hdc=::CreateCompatibleDC(NULL);
810   SelectObject(hdc,(HBITMAP)xid);
811   return hdc;
812   }
813 
814 
815 // Release it (no-op)
ReleaseDC(FXID hdc) const816 int FXBitmap::ReleaseDC(FXID hdc) const {
817   return ::DeleteDC((HDC)hdc);
818   }
819 
820 #endif
821 
822 
823 // Change options
setOptions(FXuint opts)824 void FXBitmap::setOptions(FXuint opts){
825   options=(options&~BITMAP_MASK) | (opts&BITMAP_MASK);
826   }
827 
828 
829 // Set pixel data ownership flag
setOwned(FXbool owned)830 void FXBitmap::setOwned(FXbool owned){
831   options^=((0-owned)^options)&BITMAP_OWNED;
832   }
833 
834 
835 // Get pixel ownership flag
isOwned() const836 FXbool FXBitmap::isOwned() const {
837   return (options&BITMAP_OWNED)!=0;
838   }
839 
840 
841 // Attach pixel buffer to bitmap, and assume ownership of it if BITMAP_OWNED is passed
setData(FXuchar * pix,FXuint opts)842 void FXBitmap::setData(FXuchar *pix,FXuint opts){
843 
844   // Free old data
845   if(options&BITMAP_OWNED){ freeElms(data); }
846 
847   // Only own pixel buffer if one was passed
848   if(pix && (opts&BITMAP_OWNED)){
849     options|=BITMAP_OWNED;
850     }
851   else{
852     options&=~BITMAP_OWNED;
853     }
854 
855   // Set the pointer
856   data=pix;
857   }
858 
859 
860 // Populate the bitmap with new pixel data
setData(FXuchar * pix,FXuint opts,FXint w,FXint h)861 void FXBitmap::setData(FXuchar *pix,FXuint opts,FXint w,FXint h){
862 
863   // Free old data
864   if(options&BITMAP_OWNED){ freeElms(data); }
865 
866   // Resize pixmap
867   resize(w,h);
868 
869   // Only own pixel buffer if one was passed
870   if(pix && (opts&BITMAP_OWNED)){
871     options|=BITMAP_OWNED;
872     }
873   else{
874     options&=~BITMAP_OWNED;
875     }
876 
877   // Set the pointer
878   data=pix;
879   }
880 
881 
882 // Save pixel data only
savePixels(FXStream & store) const883 FXbool FXBitmap::savePixels(FXStream& store) const {
884   FXuint size=height*bytewidth;
885   store.save(data,size);
886   return true;
887   }
888 
889 
890 // Load pixel data only
loadPixels(FXStream & store)891 FXbool FXBitmap::loadPixels(FXStream& store){
892   FXuint size=height*bytewidth;
893   if(options&BITMAP_OWNED){ freeElms(data); }
894   if(!allocElms(data,size)) return false;
895   store.load(data,size);
896   options|=BITMAP_OWNED;
897   return true;
898   }
899 
900 
901 // Save data
save(FXStream & store) const902 void FXBitmap::save(FXStream& store) const {
903   FXuchar haspixels=(data!=NULL);
904   FXDrawable::save(store);
905   store << options;
906   store << haspixels;
907   if(haspixels) savePixels(store);
908   }
909 
910 
911 // Load data
load(FXStream & store)912 void FXBitmap::load(FXStream& store){
913   FXuchar haspixels;
914   FXDrawable::load(store);
915   store >> options;
916   store >> haspixels;
917   if(haspixels) loadPixels(store);
918   }
919 
920 
921 // Clean up
~FXBitmap()922 FXBitmap::~FXBitmap(){
923   FXTRACE((100,"FXBitmap::~FXBitmap %p\n",this));
924   destroy();
925   if(options&BITMAP_OWNED){freeElms(data);}
926   data=(FXuchar*)-1L;
927   }
928 
929 }
930 
931