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

Reply via email to