blob: 927eccbe162357e32fa593a519183bd378946ac8 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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
43static int use_xdbe;
44
45static 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
67void Fl_Double_Window::show() {
68 Fl_Window::show();
69}
70
71static 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 */
82void 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
98static 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?
104char 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
112typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION;
113typedef BOOL (WINAPI* fl_alpha_blend_func)
114 (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION);
115static fl_alpha_blend_func fl_alpha_blend = NULL;
116static 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 */
123char 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
159HDC 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
170static 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
179void 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
198extern void fl_restore_clip();
199
200#elif defined(__APPLE_QUARTZ__) || defined(FL_DOXYGEN)
201
202char fl_can_do_alpha_blending() {
203 return 1;
204}
205
206Fl_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 */
224Fl_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
233static 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
239static 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 */
265void 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
273const int stack_max = 16;
274static int stack_ix = 0;
275static CGContextRef stack_gc[stack_max];
276static Window stack_window[stack_max];
277static Fl_Surface_Device *_ss;
278
279/** Send all subsequent drawing commands to this offscreen buffer.
280 \param ctx the offscreen buffer.
281 */
282void 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 */
300void 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
316extern void fl_restore_clip();
317
318#else
319# error unsupported platform
320#endif
321
322/**
323 Forces the window to be redrawn.
324*/
325void 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*/
335void 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
418void 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
440void 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*/
456Fl_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//