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