1 //
2 // "$Id: Fl_Double_Window.cxx 5829 2007-05-14 15:51:00Z matt $"
3 //
4 // Double-buffered window code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2007 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 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 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 //     http://www.fltk.org/str.php
26 //
27 
28 #include <config.h>
29 #include <FL/Fl.H>
30 #include <FL/Fl_Double_Window.H>
31 #include <FL/x.H>
32 #include <FL/fl_draw.H>
33 
34 // On systems that support double buffering "naturally" the base
35 // Fl_Window class will probably do double-buffer and this subclass
36 // does nothing.
37 
38 #if USE_XDBE
39 
40 #include <X11/extensions/Xdbe.h>
41 
42 static int use_xdbe;
43 
can_xdbe()44 static int can_xdbe() {
45   static int tried;
46   if (!tried) {
47     tried = 1;
48     int event_base, error_base;
49     if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0;
50     Drawable root = RootWindow(fl_display,fl_screen);
51     int numscreens = 1;
52     XdbeScreenVisualInfo *a = XdbeGetVisualInfo(fl_display,&root,&numscreens);
53     if (!a) return 0;
54     for (int j = 0; j < a->count; j++)
55       if (a->visinfo[j].visual == fl_visual->visualid
56 	  /*&& a->visinfo[j].perflevel > 0*/) {use_xdbe = 1; break;}
57     XdbeFreeVisualInfo(a);
58   }
59   return use_xdbe;
60 }
61 #endif
62 
show()63 void Fl_Double_Window::show() {
64   Fl_Window::show();
65 }
66 
67 #ifdef WIN32
68 
69 // Code used to switch output to an off-screen window.  See macros in
70 // win32.H which save the old state in local variables.
71 
72 typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION;
73 typedef BOOL (WINAPI* fl_alpha_blend_func)
74     (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION);
75 static fl_alpha_blend_func fl_alpha_blend = NULL;
76 static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1};
77 
78 /*
79  * This function checks if the version of MSWindows that we
80  * curently run on supports alpha blending for bitmap transfers
81  * and finds the required function if so.
82  */
fl_can_do_alpha_blending()83 char fl_can_do_alpha_blending() {
84   static char been_here = 0;
85   static char can_do = 0;
86   // do this test only once
87   if (been_here) return can_do;
88   been_here = 1;
89   // load the library that implements alpha blending
90   HMODULE hMod = LoadLibrary("MSIMG32.DLL");
91   // give up if that doesn't exist (Win95?)
92   if (!hMod) return 0;
93   // now find the blending function inside that dll
94   fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend");
95   // give up if we can't find it (Win95)
96   if (!fl_alpha_blend) return 0;
97   // we have the  call, but does our display support alpha blending?
98   HDC dc = 0L;//fl_gc;
99   // get the current or the desktop's device context
100   if (!dc) dc = GetDC(0L);
101   if (!dc) return 0;
102   // check the device capabilities flags. However GetDeviceCaps
103   // does not return anything useful, so we have to do it manually:
104 
105   HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1);
106   HDC new_gc = CreateCompatibleDC(dc);
107   int save = SaveDC(new_gc);
108   SelectObject(new_gc, bm);
109   /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101);
110   BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc);
111   RestoreDC(new_gc, save);
112   DeleteDC(new_gc);
113   DeleteObject(bm);
114 
115   if (!fl_gc) ReleaseDC(0L, dc);
116   if (alpha_ok) can_do = 1;
117   return can_do;
118 }
119 
fl_makeDC(HBITMAP bitmap)120 HDC fl_makeDC(HBITMAP bitmap) {
121   HDC new_gc = CreateCompatibleDC(fl_gc);
122   SetTextAlign(new_gc, TA_BASELINE|TA_LEFT);
123   SetBkMode(new_gc, TRANSPARENT);
124 #if USE_COLORMAP
125   if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE);
126 #endif
127   SelectObject(new_gc, bitmap);
128   return new_gc;
129 }
130 
fl_copy_offscreen(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy)131 void fl_copy_offscreen(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
132   HDC new_gc = CreateCompatibleDC(fl_gc);
133   int save = SaveDC(new_gc);
134   SelectObject(new_gc, bitmap);
135   BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
136   RestoreDC(new_gc, save);
137   DeleteDC(new_gc);
138 }
139 
fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy)140 void fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
141   HDC new_gc = CreateCompatibleDC(fl_gc);
142   int save = SaveDC(new_gc);
143   SelectObject(new_gc, bitmap);
144   BOOL alpha_ok = 0;
145   // first try to alpha blend
146   if (fl_can_do_alpha_blending())
147     alpha_ok = fl_alpha_blend(fl_gc, x, y, w, h, new_gc, srcx, srcy, w, h, blendfunc);
148   // if that failed (it shouldn,t), still copy the bitmap over, but now alpha is 1
149   if (!alpha_ok)
150     BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
151   RestoreDC(new_gc, save);
152   DeleteDC(new_gc);
153 }
154 
155 extern void fl_restore_clip();
156 
157 #elif defined(__APPLE_QD__)
158 
fl_can_do_alpha_blending()159 char fl_can_do_alpha_blending() {
160   return 0;
161 }
162 
fl_create_offscreen(int w,int h)163 GWorldPtr fl_create_offscreen(int w, int h) {
164   GWorldPtr gw;
165   Rect bounds;
166   bounds.left=0; bounds.right=w; bounds.top=0; bounds.bottom=h;
167   QDErr err = NewGWorld(&gw, 0, &bounds, 0L, 0L, 0); // 'useTempMem' should not be used (says the Carbon port manual)
168   if ( err == -108 )
169     { }
170 //    fl_message( "The application memory is low. Please increase the initial memory assignment.\n" );
171   if (err!=noErr || gw==0L) return 0L;
172   return gw;
173 }
174 
fl_copy_offscreen(int x,int y,int w,int h,GWorldPtr gWorld,int srcx,int srcy)175 void fl_copy_offscreen(int x,int y,int w,int h,GWorldPtr gWorld,int srcx,int srcy) {
176   Rect src;
177   if ( !gWorld ) return;
178   src.top = srcy; src.left = srcx; src.bottom = srcy+h; src.right = srcx+w;
179   Rect dst;
180   GrafPtr dstPort; GetPort(&dstPort);
181   dst.top = y; dst.left = x; dst.bottom = y+h; dst.right = x+w;
182   RGBColor rgb, oldbg, oldfg;
183   GetForeColor(&oldfg);
184   GetBackColor(&oldbg);
185   rgb.red = 0xffff; rgb.green = 0xffff; rgb.blue = 0xffff;
186   RGBBackColor( &rgb );
187   rgb.red = 0x0000; rgb.green = 0x0000; rgb.blue = 0x0000;
188   RGBForeColor( &rgb );
189   CopyBits(GetPortBitMapForCopyBits(gWorld), GetPortBitMapForCopyBits(dstPort), &src, &dst, srcCopy, 0L);
190   RGBBackColor(&oldbg);
191   RGBForeColor(&oldfg);
192 }
193 
fl_delete_offscreen(GWorldPtr gWorld)194 void fl_delete_offscreen(GWorldPtr gWorld) {
195   DisposeGWorld(gWorld);
196 }
197 
198 static GrafPtr prevPort;
199 static GDHandle prevGD;
200 
fl_begin_offscreen(GWorldPtr gWorld)201 void fl_begin_offscreen(GWorldPtr gWorld) {
202   GetGWorld( &prevPort, &prevGD );
203   if ( gWorld )
204   {
205     SetGWorld( gWorld, 0 ); // sets the correct port
206     PixMapHandle pm = GetGWorldPixMap(gWorld);
207     Boolean ret = LockPixels(pm);
208     if ( ret == false )
209     {
210       Rect rect;
211       GetPortBounds( gWorld, &rect );
212       UpdateGWorld( &gWorld, 0, &rect, 0, 0, 0 );
213       pm = GetGWorldPixMap( gWorld );
214       LockPixels( pm );
215     }
216     fl_window = 0;
217   }
218   fl_push_no_clip();
219 }
220 
fl_end_offscreen()221 void fl_end_offscreen() {
222   GWorldPtr currPort;
223   GDHandle currGD;
224   GetGWorld( &currPort, &currGD );
225   fl_pop_clip();
226   PixMapHandle pm = GetGWorldPixMap(currPort);
227   UnlockPixels(pm);
228   SetGWorld( prevPort, prevGD );
229   fl_window = GetWindowFromPort( prevPort );
230 }
231 
232 extern void fl_restore_clip();
233 
234 #elif defined(__APPLE_QUARTZ__)
235 
fl_can_do_alpha_blending()236 char fl_can_do_alpha_blending() {
237   return 1;
238 }
239 
fl_create_offscreen(int w,int h)240 Fl_Offscreen fl_create_offscreen(int w, int h) {
241   void *data = calloc(w*h,4);
242   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
243   CGContextRef ctx = CGBitmapContextCreate(
244     data, w, h, 8, w*4, lut, kCGImageAlphaNoneSkipLast);
245   CGColorSpaceRelease(lut);
246   return (Fl_Offscreen)ctx;
247 }
248 
fl_create_offscreen_with_alpha(int w,int h)249 Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h) {
250   void *data = calloc(w*h,4);
251   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
252   CGContextRef ctx = CGBitmapContextCreate(
253     data, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast);
254   CGColorSpaceRelease(lut);
255   return (Fl_Offscreen)ctx;
256 }
257 
fl_copy_offscreen(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy)258 void fl_copy_offscreen(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy) {
259   CGContextRef src = (CGContextRef)osrc;
260   void *data = CGBitmapContextGetData(src);
261   int sw = CGBitmapContextGetWidth(src);
262   int sh = CGBitmapContextGetHeight(src);
263   CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src);
264   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
265   CGDataProviderRef src_bytes = CGDataProviderCreateWithData( 0L, data, sw*sh*4, 0L);
266   CGImageRef img = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha,
267     src_bytes, 0L, false, kCGRenderingIntentDefault);
268   // fl_push_clip();
269   CGRect rect = { { x, y }, { w, h } };
270   Fl_X::q_begin_image(rect, srcx, srcy, sw, sh);
271   CGContextDrawImage(fl_gc, rect, img);
272   Fl_X::q_end_image();
273   CGImageRelease(img);
274   CGColorSpaceRelease(lut);
275   CGDataProviderRelease(src_bytes);
276 }
277 
fl_delete_offscreen(Fl_Offscreen ctx)278 void fl_delete_offscreen(Fl_Offscreen ctx) {
279   if (!ctx) return;
280   void *data = CGBitmapContextGetData((CGContextRef)ctx);
281   CGContextRelease((CGContextRef)ctx);
282   if (!data) return;
283   free(data);
284 }
285 
286 const int stack_max = 16;
287 static int stack_ix = 0;
288 static CGContextRef stack_gc[stack_max];
289 static Window stack_window[stack_max];
290 
fl_begin_offscreen(Fl_Offscreen ctx)291 void fl_begin_offscreen(Fl_Offscreen ctx) {
292   if (stack_ix<stack_max) {
293     stack_gc[stack_ix] = fl_gc;
294     stack_window[stack_ix] = fl_window;
295   } else
296     fprintf(stderr, "FLTK CGContext Stack overflow error\n");
297   stack_ix++;
298 
299   fl_gc = (CGContextRef)ctx;
300   fl_window = 0;
301   //fl_push_no_clip();
302   CGContextSaveGState(fl_gc);
303   Fl_X::q_fill_context();
304 }
305 
fl_end_offscreen()306 void fl_end_offscreen() {
307   Fl_X::q_release_context();
308   //fl_pop_clip();
309   if (stack_ix>0)
310     stack_ix--;
311   else
312     fprintf(stderr, "FLTK CGContext Stack underflow error\n");
313   if (stack_ix<stack_max) {
314     fl_gc = stack_gc[stack_ix];
315     fl_window = stack_window[stack_ix];
316   }
317 }
318 
319 extern void fl_restore_clip();
320 
321 #else // X11
322 
323 // maybe someone feels inclined to implement alpha blending on X11?
fl_can_do_alpha_blending()324 char fl_can_do_alpha_blending() {
325   return 0;
326 }
327 
328 #endif
329 
330 // Fl_Overlay_Window relies on flush(1) copying the back buffer to the
331 // front everywhere, even if damage() == 0, thus erasing the overlay,
332 // and leaving the clip region set to the entire window.
333 
flush()334 void Fl_Double_Window::flush() {flush(0);}
335 
flush(int eraseoverlay)336 void Fl_Double_Window::flush(int eraseoverlay) {
337   make_current(); // make sure fl_gc is non-zero
338   Fl_X *myi = Fl_X::i(this);
339   if (!myi->other_xid) {
340 #if USE_XDBE
341     if (can_xdbe()) {
342       myi->other_xid =
343         XdbeAllocateBackBufferName(fl_display, fl_xid(this), XdbeCopied);
344       myi->backbuffer_bad = 1;
345     } else
346 #endif
347 #ifdef __APPLE_QD__
348     if ( ( !QDIsPortBuffered( GetWindowPort(myi->xid) ) )
349         || force_doublebuffering_ ) {
350       myi->other_xid = fl_create_offscreen(w(), h());
351       clear_damage(FL_DAMAGE_ALL);
352     }
353 #elif defined(__APPLE_QUARTZ__)
354     if (force_doublebuffering_) {
355       myi->other_xid = fl_create_offscreen(w(), h());
356       clear_damage(FL_DAMAGE_ALL);
357     }
358 #else
359     myi->other_xid = fl_create_offscreen(w(), h());
360     clear_damage(FL_DAMAGE_ALL);
361 #endif
362   }
363 #if USE_XDBE
364   if (use_xdbe) {
365     if (myi->backbuffer_bad) {
366       // Make sure we do a complete redraw...
367       if (myi->region) {XDestroyRegion(myi->region); myi->region = 0;}
368       clear_damage(FL_DAMAGE_ALL);
369       myi->backbuffer_bad = 0;
370     }
371 
372     // Redraw as needed...
373     if (damage()) {
374       fl_clip_region(myi->region); myi->region = 0;
375       fl_window = myi->other_xid;
376       draw();
377       fl_window = myi->xid;
378     }
379 
380     // Copy contents of back buffer to window...
381     XdbeSwapInfo s;
382     s.swap_window = fl_xid(this);
383     s.swap_action = XdbeCopied;
384     XdbeSwapBuffers(fl_display, &s, 1);
385     return;
386   } else
387 #endif
388   if (damage() & ~FL_DAMAGE_EXPOSE) {
389     fl_clip_region(myi->region); myi->region = 0;
390 #ifdef WIN32
391     HDC _sgc = fl_gc;
392     fl_gc = fl_makeDC(myi->other_xid);
393     int save = SaveDC(fl_gc);
394     fl_restore_clip(); // duplicate region into new gc
395     draw();
396     RestoreDC(fl_gc, save);
397     DeleteDC(fl_gc);
398     fl_gc = _sgc;
399 #elif defined(__APPLE__)
400     if ( myi->other_xid ) {
401       fl_begin_offscreen( myi->other_xid );
402       fl_clip_region( 0 );
403       draw();
404       fl_end_offscreen();
405     } else {
406       draw();
407     }
408 #else // X:
409     fl_window = myi->other_xid;
410     draw();
411     fl_window = myi->xid;
412 #endif
413   }
414   if (eraseoverlay) fl_clip_region(0);
415   // on Irix (at least) it is faster to reduce the area copied to
416   // the current clip region:
417   int X,Y,W,H; fl_clip_box(0,0,w(),h(),X,Y,W,H);
418   if (myi->other_xid) fl_copy_offscreen(X, Y, W, H, myi->other_xid, X, Y);
419 }
420 
resize(int X,int Y,int W,int H)421 void Fl_Double_Window::resize(int X,int Y,int W,int H) {
422   int ow = w();
423   int oh = h();
424   Fl_Window::resize(X,Y,W,H);
425 #if USE_XDBE
426   if (use_xdbe) return;
427 #endif
428   Fl_X* myi = Fl_X::i(this);
429   if (myi && myi->other_xid && (ow != w() || oh != h())) {
430     fl_delete_offscreen(myi->other_xid);
431     myi->other_xid = 0;
432   }
433 }
434 
hide()435 void Fl_Double_Window::hide() {
436   Fl_X* myi = Fl_X::i(this);
437   if (myi && myi->other_xid) {
438 #if USE_XDBE
439     if (!use_xdbe)
440 #endif
441       fl_delete_offscreen(myi->other_xid);
442   }
443   Fl_Window::hide();
444 }
445 
~Fl_Double_Window()446 Fl_Double_Window::~Fl_Double_Window() {
447   hide();
448 }
449 
450 //
451 // End of "$Id: Fl_Double_Window.cxx 5829 2007-05-14 15:51:00Z matt $".
452 //
453