diff options
| author | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2019-08-18 21:56:12 +0200 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2019-08-18 21:56:12 +0200 |
| commit | 8c39007b265d978ed16adc06b7b1f56c508f7f51 (patch) | |
| tree | 517b86228b0e498a3462224c0e9e74a6e94ddb0f /src | |
| parent | 8070d645a906ef88b85a1939bea8af649bf8dbe8 (diff) | |
Improved implementation of layer-backed views under macOS ≥ 10.14
This new implementation does all screen drawing through the drawRect: method.
The benefit is that [[NSGraphicsContext currentContext] CGContext] provides
a system-built drawing context whose product ultimately appears on screen.
Feed-back from the fldigi FLTK application shows that this procedure is
measurably faster that the previous one when drawing a rapidly changing image.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_cocoa.mm | 391 |
1 files changed, 175 insertions, 216 deletions
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 07949ac57..452a3a3be 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -3,7 +3,7 @@ // // MacOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2018 by Bill Spitzak and others. +// Copyright 1998-2019 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -502,7 +502,6 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() - (void)recursivelySendToSubwindows:(SEL)sel applyToSelf:(BOOL)b; - (void)setSubwindowFrame; - (void)checkSubwindowFrame; -- (void)did_window_change_resolution; - (void)waitForExpose; - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @@ -523,6 +522,11 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() BOOL need_handle; // YES means Fl::handle(FL_KEYBOARD,) is needed after handleEvent processing NSInteger identifier; NSRange selectedRange; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 +@public + CGContextRef aux_bitmap; // alternative bitmap used when drawing out of widgets' draw methods + CGImageRef cgimg; // the current graphical content of the view +#endif } + (void)prepareEtext:(NSString*)aString; + (void)concatEtext:(NSString*)aString; @@ -564,34 +568,17 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context; #endif - (BOOL)did_view_resolution_change; -@end - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 -@interface FLViewLayer : FLView // for layer-backed non-GL windows -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 -< CALayerDelegate > -#endif -{ -@public - CGContextRef layer_data; -} -- (void)displayLayer:(CALayer *)layer; -- (void)prepare_bitmap_for_layer; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - (void)reset_layer_data; -- (BOOL)did_view_resolution_change; -- (void)drawRect:(NSRect)rect; +#endif @end -#endif //10_8 + @implementation FLWindow - (void)close { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if ([[self contentView] isMemberOfClass:[FLViewLayer class]]) { - FLViewLayer *view = (FLViewLayer*)[self contentView]; - CGContextRelease(view->layer_data); - view->layer_data = NULL; - } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA) [(FLView*)[self contentView] reset_layer_data]; #endif [super close]; // when a fullscreen window is closed, windowDidResize may be sent after the close message was sent @@ -706,9 +693,6 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() if (r->size.width == 0 && r->size.height == 0) r->origin.x = r->origin.y = 0; } d->subRect(r); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if ([view isMemberOfClass:[FLViewLayer class]]) [(FLViewLayer*)view reset_layer_data]; -#endif w->redraw(); if (fl_mac_os_version < 100900) { NSInteger parent_num = [fl_xid(w->window()) windowNumber]; @@ -719,10 +703,6 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() } } -- (void)did_window_change_resolution { - [(FLView*)[self contentView] did_view_resolution_change]; -} - -(void)waitForExpose { if ([self getFl_Window]->shown()) { @@ -1237,15 +1217,12 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *window = [nsw getFl_Window]; - if (!window->parent()) { - starting_moved_window = window; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - FLView *view = (FLView*)[nsw contentView]; - if ([view layer] && [view did_view_resolution_change]) { - [nsw recursivelySendToSubwindows:@selector(did_window_change_resolution) applyToSelf:NO]; - } -#endif + if (!window->parent()) starting_moved_window = window; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA && window->as_gl_window() && Fl::use_high_res_GL() && [(FLView*)[nsw contentView] did_view_resolution_change]) { + [[nsw contentView] setNeedsDisplay:YES]; // necessary with macOS ≥ 10.14.2; harmless before } +#endif if (window == starting_moved_window) { // we update 'main_screen_height' here because it's wrong just after screen config changes main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; @@ -1279,8 +1256,11 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; window->resize(X, Y, lround(r.size.width/s), lround(r.size.height/s)); Fl_Cocoa_Window_Driver::driver(window)->view_resized(0); update_e_xy_and_e_xy_root(nsw); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (views_use_CA && !window->as_gl_window()) [(FLViewLayer*)view reset_layer_data]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA && !window->as_gl_window()) { + [view reset_layer_data]; + window->redraw(); + } #endif fl_unlock_function(); } @@ -2157,93 +2137,40 @@ static FLTextInputContext* fltextinputcontext_instance = nil; /* Implementation note for the support of layer-backed views. MacOS 10.14 Mojave changes the way all drawing to displays is performed: - all NSView objects become layer-backed, that is, the drawing is done by + all NSView objects become layer-backed, that is, drawing is done by Core Animation to a CALayer object whose content is then displayed by the NSView. The global variable views_use_CA is set to YES when such change applies, that is, for apps running under 10.14 and linked to SDK 10.14. When views_use_CA is NO, views are not supposed to be layer-backed. - Each layer-backed non-OpenGL window has a single FLViewLayer object, derived - from FLView, which itself has an associated CALayer. - FLViewLayer implements displayLayer:. Consequently, FLViewLayer objects are drawn - by the displayLayer: method. An FLViewLayer contains also a member variable - CGContextRef layer_data, a bitmap context the size of the view (double on Retina). - All Quartz drawings go to this bitmap. displayLayer: finishes by using an image copy - of the bitmap as the layer's contents. That step fills the window. - When resized or when the window flips between low/high resolution displays, - FLViewLayer receives the reset_layer_data message which deletes the bitmap and zeroes layer_data. - This ensures the bitmap is recreated after the window was resized or changed resolution. + Most drawing is done by [FLView drawRect:] which the system calls + when a window is created or resized and when Fl_Window_Driver::flush() runs which sends the display + message to the view. Within drawRect:, [[NSGraphicsContext currentContext] CGContext] + gives a graphics context whose product ultimately appears on screen. But the + full content of the view must be redrawn each time drawRect: runs, in contrast + to pre-10.14 where drawings were added to the previous window content. + That is why FLView maintains a CGImage (view->cgimg) equal to the last content of the FLView. + At the beginning of drawRect:, cgimg is drawn to the graphics context and released, + then drawRect: does its drawing, finally cgimg is newly set to the + view's graphical content as it is at the end of drawRect:. + + A problem arises to support drawing done outside Fl_Window_Driver::flush(), that is, + after the app calls Fl_Window::make_current() at any time it wants. + That situation is identified by the condition (views_use_CA && !through_drawRect). + A graphics context usable outside drawRect: that ultimately appears on screen, + if it exists, was not identified. Thus, a bitmap graphics context (view->aux_bitmap) + is created by function prepare_bitmap_for_layer() with the size of the view + (doubled on retina). Drawing operations after the call to Fl_Window::make_current() + are directed to this bitmap. Fl_Window::make_current() also calls + [view setNeedsDisplay:YES] which instructs the system to run drawRect: + at the next event loop. Later, when drawRect: runs, it detects that drawing was + sent to aux_bitmap because that pointer is not nil. The content of aux_bitmap + is transformed to a CGImage which is assigned to view->cgimg and which is + drawn to drawRect's graphics context. - Layer-backed OpenGL windows remain processed as before. + OpenGL windows remain processed under 10.14 as before. */ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - -@implementation FLViewLayer -- (void)displayLayer:(CALayer *)layer { - // used by non-GL layer-backed views - Fl_Window *window = [(FLWindow*)[self window] getFl_Window]; - if (!window) return; // needed e.g. when closing a tab in a window - if (!layer_data) { // runs when window is created, resized, changed screen resolution - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - if (d->wait_for_expose_value) { - [super did_view_resolution_change]; - d->wait_for_expose_value = 0; - } - [self prepare_bitmap_for_layer]; - Fl_X *i = Fl_X::i(window); - if ( i->region ) { - Fl_Graphics_Driver::default_driver().XDestroyRegion(i->region); - i->region = 0; - } - window->clear_damage(FL_DAMAGE_ALL); - through_Fl_X_flush = YES; - d->Fl_Window_Driver::flush(); - Fl_Cocoa_Window_Driver::q_release_context(); - through_Fl_X_flush = NO; - window->clear_damage(); - } - if (layer_data) { - CGImageRef cgimg = CGBitmapContextCreateImage(layer_data); // requires 10.4 - layer.contents = (id)cgimg; - CGImageRelease(cgimg); - } -} -- (void)prepare_bitmap_for_layer { - Fl_Window *window = [(FLWindow*)[self window] getFl_Window]; - CALayer *layer = [self layer]; - NSRect rect = [self frame]; - layer.bounds = NSRectToCGRect(rect); - if (Fl_Cocoa_Window_Driver::driver(window)->mapped_to_retina()) { - rect.size.width *= 2; rect.size.height *= 2; - layer.contentsScale = 2.; - } else layer.contentsScale = 1.; - static CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); - layer_data = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, 4 * rect.size.width, cspace, kCGImageAlphaPremultipliedFirst); - CGContextClearRect(layer_data, NSRectToCGRect(rect)); -} -- (BOOL)did_view_resolution_change { - BOOL retval = [super did_view_resolution_change]; - if (retval) { - [self reset_layer_data]; - [self setNeedsDisplay:YES]; - } - return retval; -} --(void)reset_layer_data -{ - Fl_Window *win = [(FLWindow*)[self window] getFl_Window]; - if (win) { // can be null when window is set fullscreen - Fl_Cocoa_Window_Driver::q_release_context(Fl_Cocoa_Window_Driver::driver(win)); - win->redraw(); - } - CGContextRelease(layer_data); - layer_data = NULL; -} -- (void)drawRect:(NSRect)rect {} // necessary under 10.14.2 to support gl_start() -@end -#endif //>= MAC_OS_X_VERSION_10_8 - @implementation FLView - (BOOL)did_view_resolution_change { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @@ -2259,16 +2186,20 @@ static FLTextInputContext* fltextinputcontext_instance = nil; BOOL retval = (d->wait_for_expose_value == 0 && previous != d->mapped_to_retina()); if (retval) { d->changed_resolution(true); - if (window->as_gl_window() && views_use_CA && Fl::use_high_res_GL()) { - [self setNeedsDisplay:YES]; // necessary with macOS ≥ 10.14.2; harmless before - } } return retval; } #endif return NO; } - +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 +- (void)reset_layer_data { + CGContextRelease(aux_bitmap); + aux_bitmap = NULL; + CGImageRelease(cgimg); + cgimg = NULL; +} +#endif - (BOOL)process_keydown:(NSEvent*)theEvent { id o = fl_mac_os_version >= 100600 ? [self performSelector:@selector(inputContext)] : [FLTextInputContext singleInstance]; @@ -2284,29 +2215,39 @@ static FLTextInputContext* fltextinputcontext_instance = nil; } return self; } -- (void)displayLayer:(CALayer *)layer { // necessary with 10.14, does not run with ≥ 10.14.2 - [self drawRect:[self frame]]; -} -/* Used by non-layered windows and by all GL windows. - * Gets called when a window is created or resized, or moved between retina and non-retina displays - * (with Mac OS ≥ 10.11 also when deminiaturized) +/* Used by all GL or non-GL windows. + * Gets called when a window is created, resized, or moved between retina and non-retina displays. + * For non-GL windows, also called by Fl_Window_Driver::flush() because of the display message sent to the view. */ - (void)drawRect:(NSRect)rect { fl_lock_function(); FLWindow *cw = (FLWindow*)[self window]; Fl_Window *window = [cw getFl_Window]; - through_drawRect = YES; - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - [self did_view_resolution_change]; - if (d->wait_for_expose_value) { - d->wait_for_expose_value = 0; - if (window->as_gl_window() && views_use_CA && fl_mac_os_version < 101401) { // 1st drawing of layer-backed GL window - window->size(window->w(), window->h()); // sends message [GLcontext update] +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + CGContextRef gc = views_use_CA ? [[NSGraphicsContext currentContext] CGContext] : NULL; + if (aux_bitmap) { + CGImageRelease(cgimg); + cgimg = CGBitmapContextCreateImage(aux_bitmap); + CGContextRelease(aux_bitmap); + aux_bitmap = NULL; + if (!window->damage()) { + CGContextDrawImage(gc, NSRectToCGRect([self frame]), cgimg); + fl_unlock_function(); + return; } } +#endif + Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); if (!through_Fl_X_flush) { + [self did_view_resolution_change]; + if (d->wait_for_expose_value) { + d->wait_for_expose_value = 0; + if (window->as_gl_window() && views_use_CA && fl_mac_os_version < 101401) { // 1st drawing of layer-backed GL window + window->size(window->w(), window->h()); // sends message [GLcontext update] + } + } Fl_X *i = Fl_X::i(window); if ( i->region ) { Fl_Graphics_Driver::default_driver().XDestroyRegion(i->region); @@ -2314,8 +2255,23 @@ static FLTextInputContext* fltextinputcontext_instance = nil; } window->clear_damage(FL_DAMAGE_ALL); } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + else if (cgimg && gc) { + CGContextDrawImage(gc, NSRectToCGRect([self frame]), cgimg); + } + CGImageRelease(cgimg); + cgimg = 0; +#endif + through_drawRect = YES; d->Fl_Window_Driver::flush(); if (!through_Fl_X_flush) window->clear_damage(); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA && gc) { + CGImageRelease(cgimg); + cgimg = CGBitmapContextCreateImage(gc); + Fl_Cocoa_Window_Driver::q_release_context(); + } +#endif through_drawRect = NO; fl_unlock_function(); } @@ -2924,21 +2880,14 @@ void Fl_Cocoa_Window_Driver::flush() { if (pWindow->as_gl_window()) { Fl_Window_Driver::flush(); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - } else if (views_use_CA) { - FLViewLayer *view = (FLViewLayer*)[fl_xid(pWindow) contentView]; - if (!view->layer_data) [view prepare_bitmap_for_layer]; - through_Fl_X_flush = YES; - Fl_Window_Driver::flush(); - through_Fl_X_flush = NO; - q_release_context(); - [view setNeedsDisplay:YES]; -#endif } else { through_Fl_X_flush = YES; NSView *view = [fl_xid(pWindow) contentView]; - [view setNeedsDisplay:YES]; - [view displayIfNeededIgnoringOpacity]; + if (views_use_CA) [view display]; + else { + [view setNeedsDisplay:YES]; + [view displayIfNeededIgnoringOpacity]; + } through_Fl_X_flush = NO; } } @@ -3098,14 +3047,7 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow() x->next = NULL; Fl_X::first = x; } - FLView *myview; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (views_use_CA && !w->as_gl_window()) { - myview = [FLViewLayer alloc]; - } else -#endif - myview = [FLView alloc]; - myview = [myview initWithFrame:crect]; + FLView *myview = [[FLView alloc] initWithFrame:crect]; [cw setContentView:myview]; [myview release]; [cw setLevel:winlevel]; @@ -3334,50 +3276,51 @@ void Fl_Cocoa_Window_Driver::resize(int X, int Y, int W, int H) { } } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 +CGContextRef prepare_bitmap_for_layer(FLView *view) { + Fl_Window *window = [(FLWindow*)[view window] getFl_Window]; + NSRect rect = [view frame]; + if (Fl_Cocoa_Window_Driver::driver(window)->mapped_to_retina()) { + rect.size.width *= 2; rect.size.height *= 2; + } + static CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); + CGContextRef gc = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, 4 * rect.size.width, cspace, kCGImageAlphaPremultipliedFirst); + CGContextClearRect(gc, NSRectToCGRect(rect)); + return gc; +} +#endif /* * make all drawing go into this window (called by subclass flush() impl.) - This can be called in 3 different instances: + This can be called in 3 different situations: - 1) When a window is created or resized. - Before 10.14: macOS sends the drawRect: message to the window view after having prepared the - current graphics context to draw to this view. Variable through_drawRect is set - to YES and calls Fl_Window_Driver::flush(). - After 10.14: macOS sends the displayLayer: message to the window view. If the layer bitmap - does not exist, variable through_Fl_X_flush is set to YES and Fl_Window_Driver::flush() is called. - Fl_Window_Driver::flush() calls Fl_Window::flush() that calls Fl_Window::make_current() that - uses the graphics context of the window or the layer. The window's draw() function is then executed. - After 10.14: the displayLayer method sets the layer's contents to the layer's bitmap. + 1) When a window is created, resized or moved between low/high resolution displays. + macOS sends the drawRect: message to the window view after having prepared the + current graphics context to draw to this view. The drawRect: method sets through_drawRect + to YES and calls Fl_Window_Driver::flush(). Fl_Window_Driver::flush() calls + Fl_Window::flush() that calls Fl_Window::make_current() that uses the graphics + context of the window or the layer. The window's draw() function is then executed. 2) At each round of the FLTK event loop. Fl::flush() is called, that calls Fl_Cocoa_Window_Driver::flush() on each window that needs drawing. Fl_Cocoa_Window_Driver::flush() sets through_Fl_X_Flush to YES and marks the view as - needing display. - Before 10.14: the view is sent the displayIfNeededIgnoringOpacity message which makes - the OS send the view the drawRect: message. The program proceeds next as in 1) above. - After 10.14: Fl_Window_Driver::flush() is called which draws to the layer bitmap, and - the layered view is sent the displayLayer: message at the next event loop. + needing display. The view is sent the displayIfNeededIgnoringOpacity or display message which makes + the OS send the view the drawRect: message. The program proceeds next as in 1) above. 3) An FLTK application can call Fl_Window::make_current() at any time before it draws to a window. This occurs for instance in the idle callback function of the mandelbrot test program. Variables - through_Fl_X_flush and through_drawRect equal NO. The graphics context of the window - or the layer is obtained. - Before 10.14: Subsequent drawing requests go to the window. - After 10.14: The layered view is marked as needing display. It's sent the display message - at the next event loop. + through_Fl_X_flush and through_drawRect equal NO. + Before 10.14: The window graphics context is obtained. Subsequent drawing requests go to the window. + After 10.14: The layered view is marked as needing display. It will be sent the drawRect: message + at the next event loop. A temporary bitmap (view->aux_bitmap) is created and subsequent drawing + operations, until drawRect: runs, are sent to it. + CAUTION: it's not possible to call Fl::wait(), Fl::check() nor Fl::ready() while in the draw() function of a widget. Use an idle callback instead. */ void Fl_Cocoa_Window_Driver::make_current() { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (views_use_CA && !through_Fl_X_flush) { // detect direct calls from the app - FLViewLayer *view = (FLViewLayer*)[fl_xid(pWindow) contentView]; - if (!view->layer_data) [view prepare_bitmap_for_layer]; // necessary for progressive drawing - [view setNeedsDisplay:YES]; - } -#endif q_release_context(); Fl_X *i = Fl_X::i(pWindow); //NSLog(@"region-count=%d damage=%u",i->region?i->region->count:0, pWindow->damage()); @@ -3388,23 +3331,41 @@ void Fl_Cocoa_Window_Driver::make_current() destroy_double_buffer(); changed_resolution(false); } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (views_use_CA) { - gc = ((FLViewLayer*)[fl_window contentView])->layer_data; -# ifdef FLTK_HAVE_CAIRO - // necessary because cairo may have changed the Quartz context - CGContextSaveGState(gc); -# endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA && !through_drawRect) { // detect direct calls from the app + FLView *view = (FLView*)[fl_xid(pWindow) contentView]; + if (!view->aux_bitmap) { + view->aux_bitmap = prepare_bitmap_for_layer(view); + if (mapped_to_retina()) CGContextScaleCTM(view->aux_bitmap, 2, 2); + if (view->cgimg) CGContextDrawImage(view->aux_bitmap, NSRectToCGRect([view frame]), view->cgimg); + } + gc = view->aux_bitmap; + [view setNeedsDisplay:YES]; } else #endif { NSGraphicsContext *nsgc = through_drawRect ? [NSGraphicsContext currentContext] : [NSGraphicsContext graphicsContextWithWindow:fl_window]; static SEL gc_sel = fl_mac_os_version >= 101000 ? @selector(CGContext) : @selector(graphicsPort); gc = (CGContextRef)[nsgc performSelector:gc_sel]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (!gc) { // to support gl_start()/gl_finish() + static CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); + static CGContextRef dummy_gc = NULL; + CGContextRelease(dummy_gc); + gc = dummy_gc = CGBitmapContextCreate(NULL, 10, 10, 8, 40, cspace, kCGImageAlphaPremultipliedFirst); + } +#endif } Fl_Graphics_Driver::default_driver().gc(gc); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (views_use_CA) { + if (CGContextGetCTM(gc).d < 0) { // detect and correct double calls to make_current() + CGContextRestoreGState(gc); + CGContextRestoreGState(gc); + } + } +#endif CGContextSaveGState(gc); // native context - if (views_use_CA && mapped_to_retina()) CGContextScaleCTM(gc, 2,2); // antialiasing must be deactivated because it applies to rectangles too // and escapes even clipping!!! // it gets activated when needed (e.g., draw text) @@ -3437,12 +3398,14 @@ void Fl_Cocoa_Window_Driver::q_release_context(Fl_Cocoa_Window_Driver *x) { CGContextRef gc = (CGContextRef)Fl_Graphics_Driver::default_driver().gc(); if (x && x->shown() && x->gc != gc) return; if (!gc) return; - CGContextRestoreGState(gc); // match the CGContextSaveGState's of make_current - CGContextRestoreGState(gc); -#if defined(FLTK_HAVE_CAIRO) - if (views_use_CA) CGContextRestoreGState(gc); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if (!views_use_CA) #endif - CGContextFlush(gc); + { + CGContextRestoreGState(gc); // match the CGContextSaveGState's of make_current + CGContextRestoreGState(gc); + CGContextFlush(gc); + } Fl_Graphics_Driver::default_driver().gc(0); #if defined(FLTK_USE_CAIRO) if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately @@ -3749,7 +3712,7 @@ int Fl_Cocoa_Window_Driver::set_cursor(Fl_Cursor c) [(NSCursor*)cursor retain]; - [(NSWindow*)fl_xid(pWindow) invalidateCursorRectsForView:[(NSWindow*)fl_xid(pWindow) contentView]]; + [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; return 1; } @@ -3819,7 +3782,7 @@ int Fl_Cocoa_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int initWithImage:nsimage hotSpot:NSMakePoint(hotx, hoty)]; - [(NSWindow*)fl_xid(pWindow) invalidateCursorRectsForView:[(NSWindow*)fl_xid(pWindow) contentView]]; + [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; [bitmap release]; [nsimage release]; @@ -4234,20 +4197,23 @@ static NSBitmapImageRep* GL_rect_to_nsbitmap(Fl_Window *win, int x, int y, int w static NSBitmapImageRep* rect_to_NSBitmapImageRep_layer(Fl_Window *win, int x, int y, int w, int h) { // capture window data for layer-based views because initWithFocusedViewRect: does not work for them NSBitmapImageRep *bitmap = nil; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - CGContextRef gc = ((FLViewLayer*)[fl_xid(win) contentView])->layer_data; - if (!gc) return nil; - CGImageRef cgimg = CGBitmapContextCreateImage(gc); // requires 10.4 - int resolution = Fl_Cocoa_Window_Driver::driver(win->top_window())->mapped_to_retina() ? 2 : 1; - if (x || y || w != win->w() || h != win->h()) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + FLView *view = (FLView*)[fl_xid(win) contentView]; + if (win == Fl_Window::current() && fl_graphics_driver->gc()) { // make sure to get the current content of the view + CGImageRelease(view->cgimg); + view->cgimg = CGBitmapContextCreateImage((CGContextRef)fl_graphics_driver->gc()); + } + CGImageRef cgimg = view->cgimg; + if (!cgimg) return nil; + BOOL need_subimage = x || y || w != win->w() || h != win->h(); + if (need_subimage) { float s = Fl::screen_driver()->scale(0); + int resolution = Fl_Cocoa_Window_Driver::driver(win->top_window())->mapped_to_retina() ? 2 : 1; CGRect rect = CGRectMake(x * s * resolution, y * s * resolution, w * s * resolution, h * s * resolution); - CGImageRef cgimg2 = CGImageCreateWithImageInRect(cgimg, rect); - CGImageRelease(cgimg); - cgimg = cgimg2; + cgimg = CGImageCreateWithImageInRect(cgimg, rect); } bitmap = [[NSBitmapImageRep alloc] initWithCGImage:cgimg];//10.5 - CGImageRelease(cgimg); + if (need_subimage) CGImageRelease(cgimg); #endif return bitmap; } @@ -4390,13 +4356,6 @@ void Fl_Cocoa_Window_Driver::draw_layer_to_context(CALayer *layer, CGContextRef } void Fl_Cocoa_Window_Driver::gl_start(NSOpenGLContext *ctxt) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (views_use_CA) { - Fl_Cocoa_Window_Driver::q_release_context(); - [(FLViewLayer*)[fl_window contentView] reset_layer_data]; - [[fl_window contentView] layer].contentsScale = 1.; - } -#endif [ctxt update]; // supports window resizing } |
