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