mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-11-30 23:40:33 +00:00
Fix double-redraw of some views
+ BitBlt only the dirty part of Windows Only tested on Win32 + AGG context
This commit is contained in:
parent
e64d6aa6a3
commit
9ce7464968
228
AppKit/NSView.m
228
AppKit/NSView.m
@ -1850,7 +1850,20 @@ static NSGraphicsContext *graphicsContextForView(NSView *view){
|
||||
return needsToDrawRect;
|
||||
}
|
||||
|
||||
// We don't build the list here - it will be build during the display process
|
||||
// If we're being asked outside of the normal drawing process, then we'll return
|
||||
// the whole view area
|
||||
-(void)getRectsBeingDrawn:(const NSRect **)rects count:(NSInteger *)count {
|
||||
if (_invalidRects == 0 || _rectsBeingRedrawn == NULL) {
|
||||
*rects = &_visibleRect;
|
||||
*count = 1;
|
||||
return;
|
||||
}
|
||||
*rects = _rectsBeingRedrawn;
|
||||
*count = _rectsBeingRedrawnCount;
|
||||
}
|
||||
|
||||
-(void)buildRectsBeingDrawn:(const NSRect **)rects count:(NSInteger *)count {
|
||||
// This method returns all the rects being drawn concerning the view
|
||||
// That's all of the dirty rects from the view, but also all the ones
|
||||
// from the superview that might have caused the redraw.
|
||||
@ -1869,7 +1882,7 @@ static NSGraphicsContext *graphicsContextForView(NSView *view){
|
||||
if (opaqueAncestor != self) {
|
||||
// Ask our opaque ancestor what to draw
|
||||
const NSRect *ancestorRects;
|
||||
[opaqueAncestor getRectsBeingDrawn:&ancestorRects count:&_rectsBeingRedrawnCount];
|
||||
[opaqueAncestor buildRectsBeingDrawn:&ancestorRects count:&_rectsBeingRedrawnCount];
|
||||
if (_rectsBeingRedrawnCount) {
|
||||
_rectsBeingRedrawn = NSZoneCalloc(NULL, _rectsBeingRedrawnCount, sizeof(NSRect));
|
||||
int rectsCount = 0;
|
||||
@ -1889,7 +1902,7 @@ static NSGraphicsContext *graphicsContextForView(NSView *view){
|
||||
NSView *opaqueAncestor = [view opaqueAncestor];
|
||||
const NSRect *ancestorRects;
|
||||
NSUInteger ancestorRectsCount;
|
||||
[opaqueAncestor getRectsBeingDrawn:&ancestorRects count:&ancestorRectsCount];
|
||||
[opaqueAncestor buildRectsBeingDrawn:&ancestorRects count:&ancestorRectsCount];
|
||||
if (ancestorRectsCount + _invalidRectCount > 0) {
|
||||
_rectsBeingRedrawn = NSZoneCalloc(NULL, _invalidRectCount + ancestorRectsCount, sizeof(NSRect));
|
||||
int rectsCount = 0;
|
||||
@ -1944,73 +1957,113 @@ static NSGraphicsContext *graphicsContextForView(NSView *view){
|
||||
return [_subviews objectEnumerator];
|
||||
}
|
||||
|
||||
-(void)_displayIfNeededWithoutViewWillDraw {
|
||||
if([self needsDisplay]){
|
||||
[self displayRect:unionOfInvalidRects(self)];
|
||||
}
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
-(void)_displayRectIgnoringOpacity:(NSRect)rect {
|
||||
NSRect visibleRect=[self visibleRect];
|
||||
|
||||
NSView* subView = nil;
|
||||
while ((subView = [viewEnumerator nextObject])) {
|
||||
[subView _displayIfNeededWithoutViewWillDraw];
|
||||
clearRectsBeingRedrawn(self);
|
||||
|
||||
rect=NSIntersectionRect(rect,visibleRect);
|
||||
|
||||
if(NSIsEmptyRect(rect))
|
||||
return;
|
||||
|
||||
|
||||
if([self canDraw]){
|
||||
|
||||
const NSRect *rects;
|
||||
NSUInteger rectsCount;
|
||||
[self buildRectsBeingDrawn:&rects count:&rectsCount];
|
||||
if (rectsCount > 0) {
|
||||
// This view must be locked/unlocked prior to drawing subviews otherwise gState changes may affect
|
||||
// subviews.
|
||||
[self lockFocus];
|
||||
NSGraphicsContext *context=[NSGraphicsContext currentContext];
|
||||
CGContextRef graphicsPort=[context graphicsPort];
|
||||
NSRect r = rect;
|
||||
if (rectsCount > 1) {
|
||||
CGContextClipToRects(graphicsPort, rects, rectsCount);
|
||||
} else {
|
||||
r = NSIntersectionRect(r, rects[0]);
|
||||
CGContextClipToRect(graphicsPort, r);
|
||||
}
|
||||
[_window dirtyRect:[self convertRect:r toView:nil]];
|
||||
if ([NSGraphicsContext inQuartzDebugMode]) {
|
||||
[[NSColor yellowColor] set];
|
||||
NSRectFill(r);
|
||||
} else {
|
||||
[self drawRect:r];
|
||||
}
|
||||
[self unlockFocus];
|
||||
}
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
NSView* child = nil;
|
||||
while ((child = [viewEnumerator nextObject])) {
|
||||
NSRect check=[self convertRect:rect toView:child];
|
||||
check=NSIntersectionRect(check,[child bounds]);
|
||||
|
||||
if(!NSIsEmptyRect(check)){
|
||||
[child _displayRectIgnoringOpacity:check];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearNeedsDisplay(self);
|
||||
[_layerContext render];
|
||||
|
||||
// Don't do anything to interfere with what will be drawn in non-debug mode
|
||||
if ([NSGraphicsContext inQuartzDebugMode] == NO) {
|
||||
removeRectFromInvalidInVisibleRect(self,rect,visibleRect);
|
||||
|
||||
// Rects being drawn are only valid while we redraw
|
||||
clearRectsBeingRedrawn(self);
|
||||
|
||||
clearNeedsDisplay(self);
|
||||
}
|
||||
|
||||
/* We do the flushWindow here. If any of the display* methods are being used, you want it to update on screen immediately. If the view hierarchy is being displayed as needed at the end of an event, flushing will be disabled and this will just mark the window as needing flushing which will happen when all the views have finished being displayed */
|
||||
[[self window] flushWindow];
|
||||
}
|
||||
|
||||
-(void)displayIfNeeded {
|
||||
[self viewWillDraw];
|
||||
[self _displayIfNeededWithoutViewWillDraw];
|
||||
if ([self isOpaque] == NO) {
|
||||
id opaqueAncestor = [self opaqueAncestor];
|
||||
[opaqueAncestor displayIfNeeded];
|
||||
return;
|
||||
}
|
||||
[self displayIfNeededIgnoringOpacity];
|
||||
}
|
||||
|
||||
-(void)displayIfNeededInRect:(NSRect)rect {
|
||||
|
||||
rect=NSIntersectionRect(unionOfInvalidRects(self), rect);
|
||||
|
||||
if([self needsDisplay])
|
||||
[self displayRect:rect];
|
||||
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
|
||||
NSView* child = nil;
|
||||
while ((child = [viewEnumerator nextObject])) {
|
||||
NSRect converted=NSIntersectionRect([self convertRect:rect toView:child],[child bounds]);
|
||||
if(!NSIsEmptyRect(converted)) {
|
||||
[child displayIfNeededInRect:converted];
|
||||
}
|
||||
}
|
||||
if ([self isOpaque] == NO) {
|
||||
id opaqueAncestor = [self opaqueAncestor];
|
||||
rect = [self convertRect:rect toView:opaqueAncestor];
|
||||
[opaqueAncestor displayIfNeededInRect:rect];
|
||||
return;
|
||||
}
|
||||
[self displayIfNeededInRectIgnoringOpacity:rect];
|
||||
}
|
||||
|
||||
-(void)displayIfNeededInRectIgnoringOpacity:(NSRect)rect {
|
||||
|
||||
rect=NSIntersectionRect(unionOfInvalidRects(self), rect);
|
||||
|
||||
if([self needsDisplay])
|
||||
[self displayRectIgnoringOpacity:rect];
|
||||
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
|
||||
NSView* child = nil;
|
||||
while ((child = [viewEnumerator nextObject])) {
|
||||
NSRect converted=NSIntersectionRect([self convertRect:rect toView:child],[child bounds]);
|
||||
|
||||
if(!NSIsEmptyRect(converted)) {
|
||||
[child displayIfNeededInRectIgnoringOpacity:converted];
|
||||
}
|
||||
}
|
||||
if([self needsDisplay]) {
|
||||
[self displayRectIgnoringOpacity:rect];
|
||||
} else {
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
for (NSView* subView in viewEnumerator) {
|
||||
NSRect r = [self convertRect:rect toView:subView];
|
||||
[subView displayIfNeededInRectIgnoringOpacity:r];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)displayIfNeededIgnoringOpacity {
|
||||
|
||||
if([self needsDisplay])
|
||||
[self displayRectIgnoringOpacity:unionOfInvalidRects(self)];
|
||||
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
|
||||
NSView* child = nil;
|
||||
while ((child = [viewEnumerator nextObject])) {
|
||||
[child displayIfNeededIgnoringOpacity];
|
||||
}
|
||||
if([self needsDisplay]) {
|
||||
[self displayRectIgnoringOpacity:[self bounds]];
|
||||
} else {
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
for (NSView* subView in viewEnumerator) {
|
||||
[subView displayIfNeededIgnoringOpacity];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)displayRect:(NSRect)rect {
|
||||
@ -2021,70 +2074,11 @@ static NSGraphicsContext *graphicsContextForView(NSView *view){
|
||||
[opaque displayRectIgnoringOpacity:rect];
|
||||
}
|
||||
|
||||
-(void)displayRectIgnoringOpacity:(NSRect)rect {
|
||||
NSRect visibleRect=[self visibleRect];
|
||||
|
||||
clearRectsBeingRedrawn(self);
|
||||
|
||||
rect=NSIntersectionRect(rect,visibleRect);
|
||||
|
||||
if(NSIsEmptyRect(rect))
|
||||
return;
|
||||
|
||||
|
||||
if([self canDraw]){
|
||||
|
||||
const NSRect *rects;
|
||||
NSUInteger rectsCount;
|
||||
[self getRectsBeingDrawn:&rects count:&rectsCount];
|
||||
if (rectsCount > 0) {
|
||||
// This view must be locked/unlocked prior to drawing subviews otherwise gState changes may affect
|
||||
// subviews.
|
||||
[self lockFocus];
|
||||
NSGraphicsContext *context=[NSGraphicsContext currentContext];
|
||||
CGContextRef graphicsPort=[context graphicsPort];
|
||||
|
||||
if (rectsCount > 1) {
|
||||
CGContextClipToRect(graphicsPort,rect);
|
||||
CGContextClipToRects(graphicsPort, rects, rectsCount);
|
||||
} else {
|
||||
rect = NSIntersectionRect(rect, rects[0]);
|
||||
CGContextClipToRect(graphicsPort, rect);
|
||||
}
|
||||
if ([NSGraphicsContext inQuartzDebugMode]) {
|
||||
[[NSColor yellowColor] set];
|
||||
NSRectFill(rect);
|
||||
} else {
|
||||
[self drawRect:rect];
|
||||
}
|
||||
[self unlockFocus];
|
||||
}
|
||||
|
||||
NSEnumerator* viewEnumerator = [self _subviewsInDisplayOrderEnumerator];
|
||||
NSView* child = nil;
|
||||
while ((child = [viewEnumerator nextObject])) {
|
||||
NSRect check=[self convertRect:rect toView:child];
|
||||
|
||||
check=NSIntersectionRect(check,[child bounds]);
|
||||
|
||||
if(!NSIsEmptyRect(check)){
|
||||
[child displayRectIgnoringOpacity:check];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[_layerContext render];
|
||||
|
||||
// Don't do anything to interfere with what will be drawn in non-debug mode
|
||||
if ([NSGraphicsContext inQuartzDebugMode] == NO) {
|
||||
removeRectFromInvalidInVisibleRect(self,rect,visibleRect);
|
||||
|
||||
// Rects being drawn are only valid while we redraw
|
||||
clearRectsBeingRedrawn(self);
|
||||
}
|
||||
|
||||
/* We do the flushWindow here. If any of the display* methods are being used, you want it to update on screen immediately. If the view hierarchy is being displayed as needed at the end of an event, flushing will be disabled and this will just mark the window as needing flushing which will happen when all the views have finished being displayed */
|
||||
[[self window] flushWindow];
|
||||
// All of the of display methods end there
|
||||
-(void)displayRectIgnoringOpacity:(NSRect)rect
|
||||
{
|
||||
[self viewWillDraw];
|
||||
[self _displayRectIgnoringOpacity:rect];
|
||||
}
|
||||
|
||||
-(void)displayRectIgnoringOpacity:(NSRect)rect inContext:(NSGraphicsContext *)context {
|
||||
|
@ -42,5 +42,5 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
-(void)undo:sender;
|
||||
-(void)redo:sender;
|
||||
-(BOOL)validateMenuItem:(NSMenuItem *)item;
|
||||
|
||||
-(void)dirtyRect:(NSRect)rect;
|
||||
@end
|
||||
|
@ -1791,8 +1791,9 @@ NSString * const NSWindowDidAnimateNotification=@"NSWindowDidAnimateNotification
|
||||
doFlush=NO;
|
||||
}
|
||||
|
||||
if(doFlush)
|
||||
[[self platformWindow] flushBuffer];
|
||||
if(doFlush) {
|
||||
[[self platformWindow] flushBuffer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3136,5 +3137,9 @@ NSString * const NSWindowDidAnimateNotification=@"NSWindowDidAnimateNotification
|
||||
return _backgroundView;
|
||||
}
|
||||
|
||||
-(void)dirtyRect:(NSRect)rect
|
||||
{
|
||||
[[self platformWindow] dirtyRect:rect];
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -58,7 +58,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
id _delegate;
|
||||
NSMutableDictionary *_deviceDictionary;
|
||||
|
||||
NSRect _dirtyRect;
|
||||
}
|
||||
|
||||
-initWithFrame:(CGRect)frame styleMask:(unsigned)styleMask isPanel:(BOOL)isPanel backingType:(CGSBackingStoreType)backingType;
|
||||
|
@ -16,6 +16,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
#import <Foundation/NSPlatform_win32.h>
|
||||
|
||||
#import <AppKit/NSWindow.h>
|
||||
#import <AppKit/NSWindow-Private.h>
|
||||
#import <AppKit/NSPanel.h>
|
||||
#import <AppKit/NSDrawerWindow.h>
|
||||
#import <QuartzCore/CAWindowOpenGLContext.h>
|
||||
@ -1093,6 +1094,15 @@ static int reportGLErrorIfNeeded(const char *function,int line){
|
||||
UpdateLayeredWindow(_handle, NULL, NULL, &sizeWnd, [deviceContext dc], &ptSrc, 0, &blend, flags);
|
||||
}
|
||||
|
||||
-(void)dirtyRect:(CGRect)rect
|
||||
{
|
||||
if (_dirtyRect.size.width == 0) {
|
||||
_dirtyRect = rect;
|
||||
} else {
|
||||
_dirtyRect = CGRectUnion(rect, _dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
-(void)bitBltWindow {
|
||||
switch(_backingType){
|
||||
|
||||
@ -1114,7 +1124,35 @@ static int reportGLErrorIfNeeded(const char *function,int line){
|
||||
|
||||
if(deviceContext!=nil){
|
||||
O2SurfaceLock(surface);
|
||||
BitBlt([_cgContext dc],0,0,width,height,[deviceContext dc],left,top,SRCCOPY);
|
||||
|
||||
//#define BENCHBLIT 1 // Uncommnent this line for refresh rate debug info
|
||||
#if BENCHBLIT
|
||||
static NSTimeInterval lastTime = 0.;
|
||||
static int cptr = 0;
|
||||
cptr++;
|
||||
#endif
|
||||
NSRect r = CGRectIntegral(_dirtyRect);
|
||||
if (CGRectIsEmpty(r) == NO) {
|
||||
// Blit the dirty area
|
||||
int x = r.origin.x;
|
||||
int y = height - (r.origin.y + r.size.height); // Life would be boring without flipping
|
||||
int w = r.size.width;
|
||||
int h = r.size.height;
|
||||
BitBlt([_cgContext dc],x-left,y-top,w,h,[deviceContext dc],x,y,SRCCOPY);
|
||||
} else {
|
||||
// Blit the whole content
|
||||
BitBlt([_cgContext dc],0,0,width,height,[deviceContext dc],left,top,SRCCOPY);
|
||||
}
|
||||
// We're clean now
|
||||
_dirtyRect = CGRectZero;
|
||||
#if BENCHBLIT
|
||||
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
|
||||
if (currentTime - lastTime > 2.) {
|
||||
NSLog(@"%f fps", (double)cptr/(currentTime - lastTime));
|
||||
cptr = 0;
|
||||
lastTime = currentTime;
|
||||
}
|
||||
#endif
|
||||
O2SurfaceUnlock(surface);
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user