DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_Double_Window.cxx 8383 2011-02-06 12:20:16Z AlbrechtS $" |
| 3 | // |
| 4 | // Double-buffered window code for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 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/Fl_Printer.H> |
| 32 | #include <FL/x.H> |
| 33 | #include <FL/fl_draw.H> |
| 34 | |
| 35 | // On systems that support double buffering "naturally" the base |
| 36 | // Fl_Window class will probably do double-buffer and this subclass |
| 37 | // does nothing. |
| 38 | |
| 39 | #if USE_XDBE |
| 40 | |
| 41 | #include <X11/extensions/Xdbe.h> |
| 42 | |
| 43 | static int use_xdbe; |
| 44 | |
| 45 | static int can_xdbe() { |
| 46 | static int tried; |
| 47 | if (!tried) { |
| 48 | tried = 1; |
| 49 | int event_base, error_base; |
| 50 | if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0; |
| 51 | Drawable root = RootWindow(fl_display,fl_screen); |
| 52 | int numscreens = 1; |
| 53 | XdbeScreenVisualInfo *a = XdbeGetVisualInfo(fl_display,&root,&numscreens); |
| 54 | if (!a) return 0; |
| 55 | for (int j = 0; j < a->count; j++) { |
| 56 | if (a->visinfo[j].visual == fl_visual->visualid |
| 57 | /*&& a->visinfo[j].perflevel > 0*/) { |
| 58 | use_xdbe = 1; break; |
| 59 | } |
| 60 | } |
| 61 | XdbeFreeVisualInfo(a); |
| 62 | } |
| 63 | return use_xdbe; |
| 64 | } |
| 65 | #endif |
| 66 | |
| 67 | void Fl_Double_Window::show() { |
| 68 | Fl_Window::show(); |
| 69 | } |
| 70 | |
| 71 | static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy); |
| 72 | |
| 73 | /** \addtogroup fl_drawings |
| 74 | @{ |
| 75 | */ |
| 76 | /** Copy a rectangular area of the given offscreen buffer into the current drawing destination. |
| 77 | \param x,y position where to draw the copied rectangle |
| 78 | \param w,h size of the copied rectangle |
| 79 | \param pixmap offscreen buffer containing the rectangle to copy |
| 80 | \param srcx,srcy origin in offscreen buffer of rectangle to copy |
| 81 | */ |
| 82 | void fl_copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) { |
| 83 | if (fl_graphics_driver == Fl_Display_Device::display_device()->driver()) { |
| 84 | fl_copy_offscreen_to_display(x, y, w, h, pixmap, srcx, srcy); |
| 85 | } |
| 86 | else { // when copy is not to the display |
| 87 | fl_begin_offscreen(pixmap); |
| 88 | uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0); |
| 89 | fl_end_offscreen(); |
| 90 | fl_draw_image(img, x, y, w, h, 3, 0); |
| 91 | delete[] img; |
| 92 | } |
| 93 | } |
| 94 | /** @} */ |
| 95 | |
| 96 | #if defined(USE_X11) |
| 97 | |
| 98 | static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) { |
| 99 | XCopyArea(fl_display, pixmap, fl_window, fl_gc, srcx, srcy, w, h, x, y); |
| 100 | } |
| 101 | |
| 102 | |
| 103 | // maybe someone feels inclined to implement alpha blending on X11? |
| 104 | char fl_can_do_alpha_blending() { |
| 105 | return 0; |
| 106 | } |
| 107 | #elif defined(WIN32) |
| 108 | |
| 109 | // Code used to switch output to an off-screen window. See macros in |
| 110 | // win32.H which save the old state in local variables. |
| 111 | |
| 112 | typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION; |
| 113 | typedef BOOL (WINAPI* fl_alpha_blend_func) |
| 114 | (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION); |
| 115 | static fl_alpha_blend_func fl_alpha_blend = NULL; |
| 116 | static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1}; |
| 117 | |
| 118 | /* |
| 119 | * This function checks if the version of MSWindows that we |
| 120 | * curently run on supports alpha blending for bitmap transfers |
| 121 | * and finds the required function if so. |
| 122 | */ |
| 123 | char fl_can_do_alpha_blending() { |
| 124 | static char been_here = 0; |
| 125 | static char can_do = 0; |
| 126 | // do this test only once |
| 127 | if (been_here) return can_do; |
| 128 | been_here = 1; |
| 129 | // load the library that implements alpha blending |
| 130 | HMODULE hMod = LoadLibrary("MSIMG32.DLL"); |
| 131 | // give up if that doesn't exist (Win95?) |
| 132 | if (!hMod) return 0; |
| 133 | // now find the blending function inside that dll |
| 134 | fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend"); |
| 135 | // give up if we can't find it (Win95) |
| 136 | if (!fl_alpha_blend) return 0; |
| 137 | // we have the call, but does our display support alpha blending? |
| 138 | // get the desktop's device context |
| 139 | HDC dc = GetDC(0L); |
| 140 | if (!dc) return 0; |
| 141 | // check the device capabilities flags. However GetDeviceCaps |
| 142 | // does not return anything useful, so we have to do it manually: |
| 143 | |
| 144 | HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1); |
| 145 | HDC new_gc = CreateCompatibleDC(dc); |
| 146 | int save = SaveDC(new_gc); |
| 147 | SelectObject(new_gc, bm); |
| 148 | /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101); |
| 149 | BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc); |
| 150 | RestoreDC(new_gc, save); |
| 151 | DeleteDC(new_gc); |
| 152 | DeleteObject(bm); |
| 153 | ReleaseDC(0L, dc); |
| 154 | |
| 155 | if (alpha_ok) can_do = 1; |
| 156 | return can_do; |
| 157 | } |
| 158 | |
| 159 | HDC fl_makeDC(HBITMAP bitmap) { |
| 160 | HDC new_gc = CreateCompatibleDC(fl_gc); |
| 161 | SetTextAlign(new_gc, TA_BASELINE|TA_LEFT); |
| 162 | SetBkMode(new_gc, TRANSPARENT); |
| 163 | #if USE_COLORMAP |
| 164 | if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE); |
| 165 | #endif |
| 166 | SelectObject(new_gc, bitmap); |
| 167 | return new_gc; |
| 168 | } |
| 169 | |
| 170 | static void fl_copy_offscreen_to_display(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) { |
| 171 | HDC new_gc = CreateCompatibleDC(fl_gc); |
| 172 | int save = SaveDC(new_gc); |
| 173 | SelectObject(new_gc, bitmap); |
| 174 | BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY); |
| 175 | RestoreDC(new_gc, save); |
| 176 | DeleteDC(new_gc); |
| 177 | } |
| 178 | |
| 179 | void fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) { |
| 180 | HDC new_gc = CreateCompatibleDC(fl_gc); |
| 181 | int save = SaveDC(new_gc); |
| 182 | SelectObject(new_gc, bitmap); |
| 183 | BOOL alpha_ok = 0; |
| 184 | // first try to alpha blend |
| 185 | // if to printer, always try alpha_blend |
| 186 | int to_display = Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id; // true iff display output |
| 187 | if ( (to_display && fl_can_do_alpha_blending()) || Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) { |
| 188 | alpha_ok = fl_alpha_blend(fl_gc, x, y, w, h, new_gc, srcx, srcy, w, h, blendfunc); |
| 189 | } |
| 190 | // if that failed (it shouldn't), still copy the bitmap over, but now alpha is 1 |
| 191 | if (!alpha_ok) { |
| 192 | BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY); |
| 193 | } |
| 194 | RestoreDC(new_gc, save); |
| 195 | DeleteDC(new_gc); |
| 196 | } |
| 197 | |
| 198 | extern void fl_restore_clip(); |
| 199 | |
| 200 | #elif defined(__APPLE_QUARTZ__) || defined(FL_DOXYGEN) |
| 201 | |
| 202 | char fl_can_do_alpha_blending() { |
| 203 | return 1; |
| 204 | } |
| 205 | |
| 206 | Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h) { |
| 207 | void *data = calloc(w*h,4); |
| 208 | CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); |
| 209 | CGContextRef ctx = CGBitmapContextCreate( |
| 210 | data, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast); |
| 211 | CGColorSpaceRelease(lut); |
| 212 | return (Fl_Offscreen)ctx; |
| 213 | } |
| 214 | |
| 215 | /** \addtogroup fl_drawings |
| 216 | @{ |
| 217 | */ |
| 218 | |
| 219 | /** |
| 220 | Creation of an offscreen graphics buffer. |
| 221 | \param w,h width and height in pixels of the buffer. |
| 222 | \return the created graphics buffer. |
| 223 | */ |
| 224 | Fl_Offscreen fl_create_offscreen(int w, int h) { |
| 225 | void *data = calloc(w*h,4); |
| 226 | CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); |
| 227 | CGContextRef ctx = CGBitmapContextCreate( |
| 228 | data, w, h, 8, w*4, lut, kCGImageAlphaNoneSkipLast); |
| 229 | CGColorSpaceRelease(lut); |
| 230 | return (Fl_Offscreen)ctx; |
| 231 | } |
| 232 | |
| 233 | static void bmProviderRelease (void *src, const void *data, size_t size) { |
| 234 | CFIndex count = CFGetRetainCount(src); |
| 235 | CFRelease(src); |
| 236 | if(count == 1) free((void*)data); |
| 237 | } |
| 238 | |
| 239 | static void fl_copy_offscreen_to_display(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy) { |
| 240 | CGContextRef src = (CGContextRef)osrc; |
| 241 | void *data = CGBitmapContextGetData(src); |
| 242 | int sw = CGBitmapContextGetWidth(src); |
| 243 | int sh = CGBitmapContextGetHeight(src); |
| 244 | CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src); |
| 245 | CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); |
| 246 | // when output goes to a Quartz printercontext, release of the bitmap must be |
| 247 | // delayed after the end of the print page |
| 248 | CFRetain(src); |
| 249 | CGDataProviderRef src_bytes = CGDataProviderCreateWithData( src, data, sw*sh*4, bmProviderRelease); |
| 250 | CGImageRef img = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha, |
| 251 | src_bytes, 0L, false, kCGRenderingIntentDefault); |
| 252 | // fl_push_clip(); |
| 253 | CGRect rect = { { x, y }, { w, h } }; |
| 254 | Fl_X::q_begin_image(rect, srcx, srcy, sw, sh); |
| 255 | CGContextDrawImage(fl_gc, rect, img); |
| 256 | Fl_X::q_end_image(); |
| 257 | CGImageRelease(img); |
| 258 | CGColorSpaceRelease(lut); |
| 259 | CGDataProviderRelease(src_bytes); |
| 260 | } |
| 261 | |
| 262 | /** Deletion of an offscreen graphics buffer. |
| 263 | \param ctx the buffer to be deleted. |
| 264 | */ |
| 265 | void fl_delete_offscreen(Fl_Offscreen ctx) { |
| 266 | if (!ctx) return; |
| 267 | void *data = CGBitmapContextGetData((CGContextRef)ctx); |
| 268 | CFIndex count = CFGetRetainCount(ctx); |
| 269 | CGContextRelease((CGContextRef)ctx); |
| 270 | if(count == 1) free(data); |
| 271 | } |
| 272 | |
| 273 | const int stack_max = 16; |
| 274 | static int stack_ix = 0; |
| 275 | static CGContextRef stack_gc[stack_max]; |
| 276 | static Window stack_window[stack_max]; |
| 277 | static Fl_Surface_Device *_ss; |
| 278 | |
| 279 | /** Send all subsequent drawing commands to this offscreen buffer. |
| 280 | \param ctx the offscreen buffer. |
| 281 | */ |
| 282 | void fl_begin_offscreen(Fl_Offscreen ctx) { |
| 283 | _ss = Fl_Surface_Device::surface(); |
| 284 | Fl_Display_Device::display_device()->set_current(); |
| 285 | if (stack_ix<stack_max) { |
| 286 | stack_gc[stack_ix] = fl_gc; |
| 287 | stack_window[stack_ix] = fl_window; |
| 288 | } else |
| 289 | fprintf(stderr, "FLTK CGContext Stack overflow error\n"); |
| 290 | stack_ix++; |
| 291 | |
| 292 | fl_gc = (CGContextRef)ctx; |
| 293 | fl_window = 0; |
| 294 | CGContextSaveGState(fl_gc); |
| 295 | fl_push_no_clip(); |
| 296 | } |
| 297 | |
| 298 | /** Quit sending drawing commands to the current offscreen buffer. |
| 299 | */ |
| 300 | void fl_end_offscreen() { |
| 301 | Fl_X::q_release_context(); |
| 302 | fl_pop_clip(); |
| 303 | if (stack_ix>0) |
| 304 | stack_ix--; |
| 305 | else |
| 306 | fprintf(stderr, "FLTK CGContext Stack underflow error\n"); |
| 307 | if (stack_ix<stack_max) { |
| 308 | fl_gc = stack_gc[stack_ix]; |
| 309 | fl_window = stack_window[stack_ix]; |
| 310 | } |
| 311 | _ss->set_current(); |
| 312 | } |
| 313 | |
| 314 | /** @} */ |
| 315 | |
| 316 | extern void fl_restore_clip(); |
| 317 | |
| 318 | #else |
| 319 | # error unsupported platform |
| 320 | #endif |
| 321 | |
| 322 | /** |
| 323 | Forces the window to be redrawn. |
| 324 | */ |
| 325 | void Fl_Double_Window::flush() {flush(0);} |
| 326 | |
| 327 | /** |
| 328 | Forces the window to be redrawn. |
| 329 | \param[in] eraseoverlay non-zero to erase overlay, zero to ignore |
| 330 | |
| 331 | Fl_Overlay_Window relies on flush(1) copying the back buffer to the |
| 332 | front everywhere, even if damage() == 0, thus erasing the overlay, |
| 333 | and leaving the clip region set to the entire window. |
| 334 | */ |
| 335 | void Fl_Double_Window::flush(int eraseoverlay) { |
| 336 | make_current(); // make sure fl_gc is non-zero |
| 337 | Fl_X *myi = Fl_X::i(this); |
| 338 | if (!myi->other_xid) { |
| 339 | #if USE_XDBE |
| 340 | if (can_xdbe()) { |
| 341 | myi->other_xid = XdbeAllocateBackBufferName(fl_display, fl_xid(this), XdbeCopied); |
| 342 | myi->backbuffer_bad = 1; |
| 343 | } else |
| 344 | #endif |
| 345 | #if defined(USE_X11) || defined(WIN32) |
| 346 | myi->other_xid = fl_create_offscreen(w(), h()); |
| 347 | clear_damage(FL_DAMAGE_ALL); |
| 348 | #elif defined(__APPLE_QUARTZ__) |
| 349 | if (force_doublebuffering_) { |
| 350 | myi->other_xid = fl_create_offscreen(w(), h()); |
| 351 | clear_damage(FL_DAMAGE_ALL); |
| 352 | } |
| 353 | #else |
| 354 | # error unsupported platform |
| 355 | #endif |
| 356 | } |
| 357 | #if USE_XDBE |
| 358 | if (use_xdbe) { |
| 359 | if (myi->backbuffer_bad || eraseoverlay) { |
| 360 | // Make sure we do a complete redraw... |
| 361 | if (myi->region) {XDestroyRegion(myi->region); myi->region = 0;} |
| 362 | clear_damage(FL_DAMAGE_ALL); |
| 363 | myi->backbuffer_bad = 0; |
| 364 | } |
| 365 | |
| 366 | // Redraw as needed... |
| 367 | if (damage()) { |
| 368 | fl_clip_region(myi->region); myi->region = 0; |
| 369 | fl_window = myi->other_xid; |
| 370 | draw(); |
| 371 | fl_window = myi->xid; |
| 372 | } |
| 373 | |
| 374 | // Copy contents of back buffer to window... |
| 375 | XdbeSwapInfo s; |
| 376 | s.swap_window = fl_xid(this); |
| 377 | s.swap_action = XdbeCopied; |
| 378 | XdbeSwapBuffers(fl_display, &s, 1); |
| 379 | return; |
| 380 | } else |
| 381 | #endif |
| 382 | if (damage() & ~FL_DAMAGE_EXPOSE) { |
| 383 | fl_clip_region(myi->region); myi->region = 0; |
| 384 | #ifdef WIN32 |
| 385 | HDC _sgc = fl_gc; |
| 386 | fl_gc = fl_makeDC(myi->other_xid); |
| 387 | int save = SaveDC(fl_gc); |
| 388 | fl_restore_clip(); // duplicate region into new gc |
| 389 | draw(); |
| 390 | RestoreDC(fl_gc, save); |
| 391 | DeleteDC(fl_gc); |
| 392 | fl_gc = _sgc; |
| 393 | //# if defined(FLTK_USE_CAIRO) |
| 394 | //if Fl::cairo_autolink_context() Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately |
| 395 | //# endif |
| 396 | #elif defined(__APPLE__) |
| 397 | if ( myi->other_xid ) { |
| 398 | fl_begin_offscreen( myi->other_xid ); |
| 399 | fl_clip_region( 0 ); |
| 400 | draw(); |
| 401 | fl_end_offscreen(); |
| 402 | } else { |
| 403 | draw(); |
| 404 | } |
| 405 | #else // X: |
| 406 | fl_window = myi->other_xid; |
| 407 | draw(); |
| 408 | fl_window = myi->xid; |
| 409 | #endif |
| 410 | } |
| 411 | if (eraseoverlay) fl_clip_region(0); |
| 412 | // on Irix (at least) it is faster to reduce the area copied to |
| 413 | // the current clip region: |
| 414 | int X,Y,W,H; fl_clip_box(0,0,w(),h(),X,Y,W,H); |
| 415 | if (myi->other_xid) fl_copy_offscreen(X, Y, W, H, myi->other_xid, X, Y); |
| 416 | } |
| 417 | |
| 418 | void Fl_Double_Window::resize(int X,int Y,int W,int H) { |
| 419 | int ow = w(); |
| 420 | int oh = h(); |
| 421 | Fl_Window::resize(X,Y,W,H); |
| 422 | #if USE_XDBE |
| 423 | if (use_xdbe) { |
| 424 | Fl_X* myi = Fl_X::i(this); |
| 425 | if (myi && myi->other_xid && (ow < w() || oh < h())) { |
| 426 | // STR #2152: Deallocate the back buffer to force creation of a new one. |
| 427 | XdbeDeallocateBackBufferName(fl_display,myi->other_xid); |
| 428 | myi->other_xid = 0; |
| 429 | } |
| 430 | return; |
| 431 | } |
| 432 | #endif |
| 433 | Fl_X* myi = Fl_X::i(this); |
| 434 | if (myi && myi->other_xid && (ow != w() || oh != h())) { |
| 435 | fl_delete_offscreen(myi->other_xid); |
| 436 | myi->other_xid = 0; |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | void Fl_Double_Window::hide() { |
| 441 | Fl_X* myi = Fl_X::i(this); |
| 442 | if (myi && myi->other_xid) { |
| 443 | #if USE_XDBE |
| 444 | if (!use_xdbe) |
| 445 | #endif |
| 446 | fl_delete_offscreen(myi->other_xid); |
| 447 | } |
| 448 | Fl_Window::hide(); |
| 449 | } |
| 450 | |
| 451 | /** |
| 452 | The destructor <I>also deletes all the children</I>. This allows a |
| 453 | whole tree to be deleted at once, without having to keep a pointer to |
| 454 | all the children in the user code. |
| 455 | */ |
| 456 | Fl_Double_Window::~Fl_Double_Window() { |
| 457 | hide(); |
| 458 | } |
| 459 | |
| 460 | // |
| 461 | // End of "$Id: Fl_Double_Window.cxx 8383 2011-02-06 12:20:16Z AlbrechtS $". |
| 462 | // |