Revision: 15178 http://sourceforge.net/p/skim-app/code/15178 Author: hofman Date: 2025-05-10 15:51:27 +0000 (Sat, 10 May 2025) Log Message: ----------- Draw page in presentation view async in bitmap. Delay transition animation until the image is drawn and set.
Modified Paths: -------------- trunk/SKPresentationOptionsSheetController.m trunk/SKPresentationView.m trunk/SKTransitionController.h trunk/SKTransitionController.m Modified: trunk/SKPresentationOptionsSheetController.m =================================================================== --- trunk/SKPresentationOptionsSheetController.m 2025-05-09 22:02:52 UTC (rev 15177) +++ trunk/SKPresentationOptionsSheetController.m 2025-05-10 15:51:27 UTC (rev 15178) @@ -320,7 +320,7 @@ rect.size.height = round(0.5 * NSHeight(rect)) + 34.0; if (previewWindow == nil) { - previewWindow = [[NSPanel alloc] initWithContentRect:rect styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView backing:NSBackingStoreBuffered defer:NO]; + previewWindow = [[NSPanel alloc] initWithContentRect:rect styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView backing:NSBackingStoreBuffered defer:NO screen:[[self window] screen]]; [previewWindow setReleasedWhenClosed:NO]; [previewWindow setTitlebarAppearsTransparent:YES]; [previewWindow setHidesOnDeactivate:NO]; @@ -363,6 +363,7 @@ CGFloat titleHeight = NSHeight([previewWindow frame]) - NSHeight([previewWindow contentLayoutRect]); rect.size.height += titleHeight - 28.0; [previewWindow setFrame:rect display:NO]; + [previewWindow layoutIfNeeded]; [previewWindow center]; [previewView setPage:[[controller pdfDocument] pageAtIndex:idx]]; Modified: trunk/SKPresentationView.m =================================================================== --- trunk/SKPresentationView.m 2025-05-09 22:02:52 UTC (rev 15177) +++ trunk/SKPresentationView.m 2025-05-10 15:51:27 UTC (rev 15178) @@ -80,6 +80,7 @@ static NSInteger navigationMode = SKNavigationBottom; @interface SKPDFPageView () <CALayerDelegate> +- (void)displayPage:(void (^)(void))completionHandler; @end @implementation SKPDFPageView @@ -101,7 +102,7 @@ CALayer *layer = [CALayer layer]; [layer setMasksToBounds:YES]; [layer setFrame:NSRectToCGRect([self bounds])]; - [layer setDelegate:self]; + [layer setActions:@{@"contents": [NSNull null]}]; pageView = [[NSView alloc] initWithFrame:[self bounds]]; [pageView setLayer:layer]; [pageView setWantsLayer:YES]; @@ -145,7 +146,7 @@ - (void)setPage:(PDFPage *)newPage { if (newPage != page) { page = newPage; - [[pageView layer] setNeedsDisplay]; + [self displayPage:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:SKPresentationViewPageChangedNotification object:self]; } } @@ -171,7 +172,11 @@ if ([transitionController pageTransitions] == nil && ([[transitionController transition] style] == SKNoTransition || equalStrings([page label], [toPage label]))) return NO; - [transitionController animateView:self forRect:[self pageRect:page] toRect:[self pageRect:toPage] atIndex:idx forward:forward change:^{ [self setPage:toPage]; }]; + [transitionController animateView:self forRect:[self pageRect:page] toRect:[self pageRect:toPage] atIndex:idx forward:forward change:^(void (^done)(void)){ + page = toPage; + [self displayPage:done]; + [[NSNotificationCenter defaultCenter] postNotificationName:SKPresentationViewPageChangedNotification object:self]; + }]; return YES; } @@ -195,31 +200,77 @@ } } -#pragma mark CALayer delegate +#pragma mark Drawing -- (void)drawLayer:(CALayer *)aLayer inContext:(CGContextRef)context { - if (page == nil) +- (void)displayPage:(void (^)(void))completionHandler { + if (page == nil) { + [[pageView layer] setContents:nil]; + if (completionHandler) + completionHandler(); return; + } + + static dispatch_queue_t drawingQueue = nil; + if (drawingQueue == nil) { + dispatch_queue_attr_t queuePriority = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_UTILITY, 0); + drawingQueue = dispatch_queue_create("net.sourceforge.skim-app.skim.pageView", queuePriority); + } + NSRect bounds = [self bounds]; - NSRect pageRect = [page boundsForBox:kPDFDisplayBoxCropBox]; - if (([page rotation] % 180) != 0) - pageRect = NSMakeRect(0.0, 0.0, NSHeight(pageRect), NSWidth(pageRect)); - CGFloat scale = [self autoScales] ? fmin(NSHeight(bounds) / NSHeight(pageRect), NSWidth(bounds) / NSWidth(pageRect)) : 1.0; - pageRect = NSInsetRect(bounds, 0.5 * (NSWidth(bounds) - scale * NSWidth(pageRect)), 0.5 * (NSHeight(bounds) - scale * NSHeight(pageRect))); - CGContextSaveGState(context); - CGContextSetFillColorWithColor(context, CGColorGetConstantColor(kCGColorWhite)); - CGContextFillRect(context, SKPixelAlignedRect(NSRectToCGRect(pageRect), context)); - CGContextRestoreGState(context); - CGContextSaveGState(context); - CGContextSetInterpolationQuality(context, [[NSUserDefaults standardUserDefaults] integerForKey:SKInterpolationQualityKey] + 1); - CGContextTranslateCTM(context, NSMinX(pageRect), NSMinY(pageRect)); - CGContextScaleCTM(context, scale, scale); - [page drawWithBox:kPDFDisplayBoxCropBox toContext:context]; - CGContextRestoreGState(context); + NSBitmapImageRep *imageRep = [self bitmapImageRepForCachingDisplayInRect:bounds]; + + if (imageRep == nil) { + if (completionHandler) + completionHandler(); + return; + } + + PDFPage *thePage = page; + BOOL autoScales = [self autoScales]; + + dispatch_async(drawingQueue, ^{ + + NSRect pageRect = [thePage boundsForBox:kPDFDisplayBoxCropBox]; + if (([thePage rotation] % 180) != 0) + pageRect = NSMakeRect(0.0, 0.0, NSHeight(pageRect), NSWidth(pageRect)); + CGFloat scale = autoScales ? fmin(NSHeight(bounds) / NSHeight(pageRect), NSWidth(bounds) / NSWidth(pageRect)) : 1.0; + pageRect = NSInsetRect(bounds, 0.5 * (NSWidth(bounds) - scale * NSWidth(pageRect)), 0.5 * (NSHeight(bounds) - scale * NSHeight(pageRect))); + CGContextRef context = [[NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep] CGContext]; + + CGContextSaveGState(context); + CGContextSetFillColorWithColor(context, CGColorGetConstantColor(kCGColorWhite)); + CGContextFillRect(context, SKPixelAlignedRect(NSRectToCGRect(pageRect), context)); + CGContextRestoreGState(context); + CGContextSaveGState(context); + CGContextSetInterpolationQuality(context, [[NSUserDefaults standardUserDefaults] integerForKey:SKInterpolationQualityKey] + 1); + CGContextTranslateCTM(context, NSMinX(pageRect), NSMinY(pageRect)); + CGContextScaleCTM(context, scale, scale); + [thePage drawWithBox:kPDFDisplayBoxCropBox toContext:context]; + CGContextRestoreGState(context); + + NSImage *image = [[NSImage alloc] initWithSize:bounds.size]; + [image addRepresentation:imageRep]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + if (thePage == page) + [[pageView layer] setContents:image]; + if (completionHandler) + completionHandler(); + + }); + + }); } -- (BOOL)layer:(CALayer *)aLayer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window { - return YES; +- (NSBitmapImageRep *)bitmapImageRepCachingDisplay { + NSImage *image = [[pageView layer] contents]; + if ([image isKindOfClass:[NSImage class]]) { + NSImageRep *imageRep = [[image representations] firstObject]; + if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) + return (NSBitmapImageRep *)imageRep; + } + return [super bitmapImageRepCachingDisplay]; } @end @@ -267,12 +318,12 @@ - (void)viewDidEndLiveResize { [super viewDidEndLiveResize]; - [[pageView layer] setNeedsDisplay]; + [self displayPage:nil]; } - (void)updatedAnnotationOnPage:(PDFPage *)aPage { if (page == aPage) - [[pageView layer] setNeedsDisplay]; + [self displayPage:nil]; } #pragma mark Accessors @@ -289,7 +340,7 @@ if (flag != pvFlags.autoScales) { pvFlags.autoScales = flag; [pageView setLayerContentsPlacement:flag ? NSViewLayerContentsPlacementScaleProportionallyToFill : NSViewLayerContentsPlacementCenter]; - [[pageView layer] setNeedsDisplay]; + [self displayPage:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:SKPresentationViewAutoScalesChangedNotification object:self]; } } Modified: trunk/SKTransitionController.h =================================================================== --- trunk/SKTransitionController.h 2025-05-09 22:02:52 UTC (rev 15177) +++ trunk/SKTransitionController.h 2025-05-10 15:51:27 UTC (rev 15178) @@ -56,7 +56,7 @@ @property (nonatomic) BOOL shouldScale; -- (void)animateView:(NSView *)view forRect:(NSRect)rect toRect:(NSRect)toRect atIndex:(NSUInteger)anIndex forward:(BOOL)forward change:(void (^)(void))change; +- (void)animateView:(NSView *)view forRect:(NSRect)rect toRect:(NSRect)toRect atIndex:(NSUInteger)anIndex forward:(BOOL)forward change:(void (^)(void (^ _Nullable done)(void)))change; @end Modified: trunk/SKTransitionController.m =================================================================== --- trunk/SKTransitionController.m 2025-05-09 22:02:52 UTC (rev 15177) +++ trunk/SKTransitionController.m 2025-05-10 15:51:27 UTC (rev 15178) @@ -171,9 +171,9 @@ return image; } -- (void)animateView:(NSView *)view forRect:(NSRect)rect toRect:(NSRect)toRect atIndex:(NSUInteger)idx forward:(BOOL)forward change:(void (^)(void))change { +- (void)animateView:(NSView *)view forRect:(NSRect)rect toRect:(NSRect)toRect atIndex:(NSUInteger)idx forward:(BOOL)forward change:(void (^)(void (^done)(void)))change { if (animating) { - change(); + change(nil); return; } @@ -183,7 +183,7 @@ if ([currentTransition style] == SKNoTransition) { - change(); + change(nil); } else { @@ -191,44 +191,46 @@ CIImage *initialImage = [self currentImageForRect:rect inView:view scale:NULL]; - change(); + change(^{ + + NSRect bounds = [view bounds]; + CGFloat imageScale = 1.0; + CIImage *finalImage = [self currentImageForRect:toRect inView:view scale:&imageScale]; + CGRect cgRect = CGRectIntegral(scaleRect(NSIntersectionRect(NSUnionRect(rect, toRect), bounds), imageScale)); + CGRect cgBounds = scaleRect(bounds, imageScale); + CGRect extent = [currentTransition shouldRestrict] ? cgRect : cgBounds; + NSString *filterName = [currentTransition styleName]; + CGFloat scale = shouldScale ? imageScale * NSHeight(bounds) / NSHeight([[[view window] screen] frame]) : imageScale; + CIFilter *transitionFilter = [self transitionFilterWithName:filterName + rect:cgRect + extent:extent + scale:scale + forward:forward + initialImage:initialImage + finalImage:finalImage]; + + if (transitionView == nil) + transitionView = [[SKTransitionView alloc] initWithFrame:bounds]; + else + [transitionView setFrame:bounds]; + [transitionView setExtent:cgBounds]; + [transitionView setFilter:transitionFilter]; + [view addSubview:transitionView positioned:NSWindowAbove relativeTo:nil]; - NSRect bounds = [view bounds]; - CGFloat imageScale = 1.0; - CIImage *finalImage = [self currentImageForRect:toRect inView:view scale:&imageScale]; - CGRect cgRect = CGRectIntegral(scaleRect(NSIntersectionRect(NSUnionRect(rect, toRect), bounds), imageScale)); - CGRect cgBounds = scaleRect(bounds, imageScale); - CGRect extent = [currentTransition shouldRestrict] ? cgRect : cgBounds; - NSString *filterName = [currentTransition styleName]; - CGFloat scale = shouldScale ? imageScale * NSHeight(bounds) / NSHeight([[[view window] screen] frame]) : imageScale; - CIFilter *transitionFilter = [self transitionFilterWithName:filterName - rect:cgRect - extent:extent - scale:scale - forward:forward - initialImage:initialImage - finalImage:finalImage]; + // Update the view and its window, so it shows the correct state when it is shown. + [view display]; + + [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){ + [context setDuration:[currentTransition duration]]; + [[transitionView animator] setProgress:1.0]; + } completionHandler:^{ + [transitionView removeFromSuperview]; + [transitionView setFilter:nil]; + animating = NO; + }]; + + }); - if (transitionView == nil) - transitionView = [[SKTransitionView alloc] initWithFrame:bounds]; - else - [transitionView setFrame:bounds]; - [transitionView setExtent:cgBounds]; - [transitionView setFilter:transitionFilter]; - [view addSubview:transitionView positioned:NSWindowAbove relativeTo:nil]; - - // Update the view and its window, so it shows the correct state when it is shown. - [view display]; - - [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){ - [context setDuration:[currentTransition duration]]; - [[transitionView animator] setProgress:1.0]; - } completionHandler:^{ - [transitionView removeFromSuperview]; - [transitionView setFilter:nil]; - animating = NO; - }]; - } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ Skim-app-commit mailing list Skim-app-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/skim-app-commit