fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Double_Window.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Double_Window.cxx 8198 2011-01-06 10:24:58Z manolo $"
00003 //
00004 // Double-buffered window code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 #include <config.h>
00029 #include <FL/Fl.H>
00030 #include <FL/Fl_Double_Window.H>
00031 #include <FL/Fl_Printer.H>
00032 #include <FL/x.H>
00033 #include <FL/fl_draw.H>
00034 
00035 // On systems that support double buffering "naturally" the base
00036 // Fl_Window class will probably do double-buffer and this subclass
00037 // does nothing.
00038 
00039 #if USE_XDBE
00040 
00041 #include <X11/extensions/Xdbe.h>
00042 
00043 static int use_xdbe;
00044 
00045 static int can_xdbe() {
00046   static int tried;
00047   if (!tried) {
00048     tried = 1;
00049     int event_base, error_base;
00050     if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0;
00051     Drawable root = RootWindow(fl_display,fl_screen);
00052     int numscreens = 1;
00053     XdbeScreenVisualInfo *a = XdbeGetVisualInfo(fl_display,&root,&numscreens);
00054     if (!a) return 0;
00055     for (int j = 0; j < a->count; j++)
00056       if (a->visinfo[j].visual == fl_visual->visualid
00057           /*&& a->visinfo[j].perflevel > 0*/) {use_xdbe = 1; break;}
00058     XdbeFreeVisualInfo(a);
00059   }
00060   return use_xdbe;
00061 }
00062 #endif
00063 
00064 void Fl_Double_Window::show() {
00065   Fl_Window::show();
00066 }
00067 
00068 static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy);
00069 
00079 void fl_copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) {
00080   if( fl_graphics_driver == fl_display_device->driver()) {
00081     fl_copy_offscreen_to_display(x, y, w, h, pixmap, srcx, srcy);
00082   }
00083   else { // when copy is not to the display
00084     fl_begin_offscreen(pixmap);
00085     uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0);
00086     fl_end_offscreen();
00087     fl_draw_image(img, x, y, w, h, 3, 0);
00088     delete img;
00089   }
00090 }
00093 #if defined(USE_X11)
00094 
00095 static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) {
00096     XCopyArea(fl_display, pixmap, fl_window, fl_gc, srcx, srcy, w, h, x, y);
00097 }
00098 
00099 
00100 // maybe someone feels inclined to implement alpha blending on X11?
00101 char fl_can_do_alpha_blending() {
00102   return 0;
00103 }
00104 #elif defined(WIN32)
00105 
00106 // Code used to switch output to an off-screen window.  See macros in
00107 // win32.H which save the old state in local variables.
00108 
00109 typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION;
00110 typedef BOOL (WINAPI* fl_alpha_blend_func)
00111     (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION);
00112 static fl_alpha_blend_func fl_alpha_blend = NULL;
00113 static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1};
00114 
00115 /*
00116  * This function checks if the version of MSWindows that we
00117  * curently run on supports alpha blending for bitmap transfers
00118  * and finds the required function if so.
00119  */
00120 char fl_can_do_alpha_blending() {
00121   static char been_here = 0;
00122   static char can_do = 0;
00123   // do this test only once
00124   if (been_here) return can_do;
00125   been_here = 1;
00126   // load the library that implements alpha blending
00127   HMODULE hMod = LoadLibrary("MSIMG32.DLL");
00128   // give up if that doesn't exist (Win95?)
00129   if (!hMod) return 0;
00130   // now find the blending function inside that dll
00131   fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend");
00132   // give up if we can't find it (Win95)
00133   if (!fl_alpha_blend) return 0;
00134   // we have the call, but does our display support alpha blending?
00135   // get the desktop's device context
00136   HDC dc = GetDC(0L);
00137   if (!dc) return 0;
00138   // check the device capabilities flags. However GetDeviceCaps
00139   // does not return anything useful, so we have to do it manually:
00140 
00141   HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1);
00142   HDC new_gc = CreateCompatibleDC(dc);
00143   int save = SaveDC(new_gc);
00144   SelectObject(new_gc, bm);
00145   /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101);
00146   BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc);
00147   RestoreDC(new_gc, save);
00148   DeleteDC(new_gc);
00149   DeleteObject(bm);
00150   ReleaseDC(0L, dc);
00151 
00152   if (alpha_ok) can_do = 1;
00153   return can_do;
00154 }
00155 
00156 HDC fl_makeDC(HBITMAP bitmap) {
00157   HDC new_gc = CreateCompatibleDC(fl_gc);
00158   SetTextAlign(new_gc, TA_BASELINE|TA_LEFT);
00159   SetBkMode(new_gc, TRANSPARENT);
00160 #if USE_COLORMAP
00161   if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE);
00162 #endif
00163   SelectObject(new_gc, bitmap);
00164   return new_gc;
00165 }
00166 
00167 static void fl_copy_offscreen_to_display(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
00168   HDC new_gc = CreateCompatibleDC(fl_gc);
00169   int save = SaveDC(new_gc);
00170   SelectObject(new_gc, bitmap);
00171   BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
00172   RestoreDC(new_gc, save);
00173   DeleteDC(new_gc);
00174 }
00175 
00176 void fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
00177   HDC new_gc = CreateCompatibleDC(fl_gc);
00178   int save = SaveDC(new_gc);
00179   SelectObject(new_gc, bitmap);
00180   BOOL alpha_ok = 0;
00181   // first try to alpha blend
00182   // if to printer, always try alpha_blend
00183   int to_display = Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id; // true iff display output
00184   if ( (to_display && fl_can_do_alpha_blending()) || Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) {
00185     alpha_ok = fl_alpha_blend(fl_gc, x, y, w, h, new_gc, srcx, srcy, w, h, blendfunc);
00186     }
00187   // if that failed (it shouldn't), still copy the bitmap over, but now alpha is 1
00188   if (!alpha_ok) {
00189     BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
00190     }
00191   RestoreDC(new_gc, save);
00192   DeleteDC(new_gc);
00193 }
00194 
00195 extern void fl_restore_clip();
00196 
00197 #elif defined(__APPLE_QUARTZ__) || defined(FL_DOXYGEN)
00198 
00199 char fl_can_do_alpha_blending() {
00200   return 1;
00201 }
00202 
00203 Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h) {
00204   void *data = calloc(w*h,4);
00205   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
00206   CGContextRef ctx = CGBitmapContextCreate(
00207     data, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast);
00208   CGColorSpaceRelease(lut);
00209   return (Fl_Offscreen)ctx;
00210 }
00211 
00221 Fl_Offscreen fl_create_offscreen(int w, int h) {
00222   void *data = calloc(w*h,4);
00223   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
00224   CGContextRef ctx = CGBitmapContextCreate(
00225     data, w, h, 8, w*4, lut, kCGImageAlphaNoneSkipLast);
00226   CGColorSpaceRelease(lut);
00227   return (Fl_Offscreen)ctx;
00228 }
00229 
00230 static void bmProviderRelease (void *src, const void *data, size_t size)
00231 {
00232   CFIndex count = CFGetRetainCount(src);
00233   CFRelease(src);
00234   if(count == 1) free((void*)data);
00235 }
00236 
00237 static void fl_copy_offscreen_to_display(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy) {
00238   CGContextRef src = (CGContextRef)osrc;
00239   void *data = CGBitmapContextGetData(src);
00240   int sw = CGBitmapContextGetWidth(src);
00241   int sh = CGBitmapContextGetHeight(src);
00242   CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src);
00243   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
00244   // when output goes to a Quartz printercontext, release of the bitmap must be
00245   // delayed after the end of the print page
00246   CFRetain(src);
00247   CGDataProviderRef src_bytes = CGDataProviderCreateWithData( src, data, sw*sh*4, bmProviderRelease);
00248   CGImageRef img = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha,
00249     src_bytes, 0L, false, kCGRenderingIntentDefault);
00250   // fl_push_clip();
00251   CGRect rect = { { x, y }, { w, h } };
00252   Fl_X::q_begin_image(rect, srcx, srcy, sw, sh);
00253   CGContextDrawImage(fl_gc, rect, img);
00254   Fl_X::q_end_image();
00255   CGImageRelease(img);
00256   CGColorSpaceRelease(lut);
00257   CGDataProviderRelease(src_bytes);
00258 }
00259 
00263 void fl_delete_offscreen(Fl_Offscreen ctx) {
00264   if (!ctx) return;
00265   void *data = CGBitmapContextGetData((CGContextRef)ctx);
00266   CFIndex count = CFGetRetainCount(ctx);
00267   CGContextRelease((CGContextRef)ctx);
00268   if(count == 1) free(data);
00269 }
00270 
00271 const int stack_max = 16;
00272 static int stack_ix = 0;
00273 static CGContextRef stack_gc[stack_max];
00274 static Window stack_window[stack_max];
00275 static Fl_Surface_Device *_ss;
00276 
00280 void fl_begin_offscreen(Fl_Offscreen ctx) {
00281   _ss = fl_surface; 
00282   fl_display_device->set_current();
00283   if (stack_ix<stack_max) {
00284     stack_gc[stack_ix] = fl_gc;
00285     stack_window[stack_ix] = fl_window;
00286   } else 
00287     fprintf(stderr, "FLTK CGContext Stack overflow error\n");
00288   stack_ix++;
00289 
00290   fl_gc = (CGContextRef)ctx;
00291   fl_window = 0;
00292   CGContextSaveGState(fl_gc);
00293   fl_push_no_clip();
00294 }
00295 
00298 void fl_end_offscreen() {
00299   Fl_X::q_release_context();
00300   fl_pop_clip();
00301   if (stack_ix>0)
00302     stack_ix--;
00303   else
00304     fprintf(stderr, "FLTK CGContext Stack underflow error\n");
00305   if (stack_ix<stack_max) {
00306     fl_gc = stack_gc[stack_ix];
00307     fl_window = stack_window[stack_ix];
00308   }
00309   _ss->set_current();
00310 }
00311 
00314 extern void fl_restore_clip();
00315 
00316 #else
00317 # error unsupported platform
00318 #endif
00319 
00323 void Fl_Double_Window::flush() {flush(0);}
00324 
00333 void Fl_Double_Window::flush(int eraseoverlay) {
00334   make_current(); // make sure fl_gc is non-zero
00335   Fl_X *myi = Fl_X::i(this);
00336   if (!myi->other_xid) {
00337 #if USE_XDBE
00338     if (can_xdbe()) {
00339       myi->other_xid =
00340         XdbeAllocateBackBufferName(fl_display, fl_xid(this), XdbeCopied);
00341       myi->backbuffer_bad = 1;
00342     } else
00343 #endif
00344 #if defined(USE_X11) || defined(WIN32)
00345     myi->other_xid = fl_create_offscreen(w(), h());
00346     clear_damage(FL_DAMAGE_ALL);
00347 #elif defined(__APPLE_QUARTZ__)
00348     if (force_doublebuffering_) {
00349       myi->other_xid = fl_create_offscreen(w(), h());
00350       clear_damage(FL_DAMAGE_ALL);
00351     }
00352 #else
00353 # error unsupported platform
00354 #endif
00355   }
00356 #if USE_XDBE
00357   if (use_xdbe) {
00358     if (myi->backbuffer_bad || eraseoverlay) {
00359       // Make sure we do a complete redraw...
00360       if (myi->region) {XDestroyRegion(myi->region); myi->region = 0;}
00361       clear_damage(FL_DAMAGE_ALL);
00362       myi->backbuffer_bad = 0;
00363     }
00364 
00365     // Redraw as needed...
00366     if (damage()) {
00367       fl_clip_region(myi->region); myi->region = 0;
00368       fl_window = myi->other_xid;
00369       draw();
00370       fl_window = myi->xid;
00371     }
00372 
00373     // Copy contents of back buffer to window...
00374     XdbeSwapInfo s;
00375     s.swap_window = fl_xid(this);
00376     s.swap_action = XdbeCopied;
00377     XdbeSwapBuffers(fl_display, &s, 1);
00378     return;
00379   } else
00380 #endif
00381   if (damage() & ~FL_DAMAGE_EXPOSE) {
00382     fl_clip_region(myi->region); myi->region = 0;
00383 #ifdef WIN32
00384     HDC _sgc = fl_gc;
00385     fl_gc = fl_makeDC(myi->other_xid);
00386     int save = SaveDC(fl_gc);
00387     fl_restore_clip(); // duplicate region into new gc
00388     draw();
00389     RestoreDC(fl_gc, save);
00390     DeleteDC(fl_gc);
00391     fl_gc = _sgc;
00392     //# if defined(FLTK_USE_CAIRO)
00393     //if Fl::cairo_autolink_context() Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately
00394     //# endif
00395 #elif defined(__APPLE__)
00396     if ( myi->other_xid ) {
00397       fl_begin_offscreen( myi->other_xid );
00398       fl_clip_region( 0 );   
00399       draw();
00400       fl_end_offscreen();
00401     } else {
00402       draw();
00403     }
00404 #else // X:
00405     fl_window = myi->other_xid;
00406     draw();
00407     fl_window = myi->xid;
00408 #endif
00409   }
00410   if (eraseoverlay) fl_clip_region(0);
00411   // on Irix (at least) it is faster to reduce the area copied to
00412   // the current clip region:
00413   int X,Y,W,H; fl_clip_box(0,0,w(),h(),X,Y,W,H);
00414   if (myi->other_xid) fl_copy_offscreen(X, Y, W, H, myi->other_xid, X, Y);
00415 }
00416 
00417 void Fl_Double_Window::resize(int X,int Y,int W,int H) {
00418   int ow = w();
00419   int oh = h();
00420   Fl_Window::resize(X,Y,W,H);
00421 #if USE_XDBE
00422   if (use_xdbe) {
00423     Fl_X* myi = Fl_X::i(this);
00424     if (myi && myi->other_xid && (ow < w() || oh < h())) {
00425       // STR #2152: Deallocate the back buffer to force creation of a new one.
00426       XdbeDeallocateBackBufferName(fl_display,myi->other_xid);
00427       myi->other_xid = 0;
00428     }
00429     return;
00430   }
00431 #endif
00432   Fl_X* myi = Fl_X::i(this);
00433   if (myi && myi->other_xid && (ow != w() || oh != h())) {
00434     fl_delete_offscreen(myi->other_xid);
00435     myi->other_xid = 0;
00436   }
00437 }
00438 
00439 void Fl_Double_Window::hide() {
00440   Fl_X* myi = Fl_X::i(this);
00441   if (myi && myi->other_xid) {
00442 #if USE_XDBE
00443     if (!use_xdbe)
00444 #endif
00445       fl_delete_offscreen(myi->other_xid);
00446   }
00447   Fl_Window::hide();
00448 }
00449 
00455 Fl_Double_Window::~Fl_Double_Window() {
00456   hide();
00457 }
00458 
00459 //
00460 // End of "$Id: Fl_Double_Window.cxx 8198 2011-01-06 10:24:58Z manolo $".
00461 //