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