Revision: 15217
          http://sourceforge.net/p/skim-app/code/15217
Author:   hofman
Date:     2025-05-16 14:40:23 +0000 (Fri, 16 May 2025)
Log Message:
-----------
Draw image for next page in presentation view on a background queue, so it will 
be available immediately on goToNextPage:

Modified Paths:
--------------
    trunk/SKPresentationView.h
    trunk/SKPresentationView.m

Modified: trunk/SKPresentationView.h
===================================================================
--- trunk/SKPresentationView.h  2025-05-15 21:22:20 UTC (rev 15216)
+++ trunk/SKPresentationView.h  2025-05-16 14:40:23 UTC (rev 15217)
@@ -73,6 +73,7 @@
 #pragma mark -
 
 @interface SKPresentationView : SKPDFPageView {
+    NSMapTable *predrawnImages;
     SKNavigationWindow *navWindow;
     SKCursorStyleWindow *cursorWindow;
     NSInteger laserPointerColor;

Modified: trunk/SKPresentationView.m
===================================================================
--- trunk/SKPresentationView.m  2025-05-15 21:22:20 UTC (rev 15216)
+++ trunk/SKPresentationView.m  2025-05-16 14:40:23 UTC (rev 15217)
@@ -146,8 +146,8 @@
     CGFloat scale = [[self window] backingScaleFactor];
     if (fabs([pageLayer contentsScale] - scale) > 0.0) {
         [pageLayer setContentsScale:scale];
-        if (page)
-            [self displayPage:nil];
+        [self removePredrawnImageAtIndex:NSNotFound];
+        [self displayPage:nil];
     }
 }
 
@@ -172,7 +172,14 @@
 
 - (void)displayPage:(PDFPage *)newPage completionHandler:(void 
(^)(void))completionHandler {
     page = newPage;
-    [self displayPage:completionHandler];
+    if (page) {
+        [self displayPage:completionHandler];
+    } else {
+        [self removePredrawnImageAtIndex:NSNotFound];
+        [pageLayer setContents:nil];
+        if (completionHandler)
+            completionHandler();
+    }
     [[NSNotificationCenter defaultCenter] 
postNotificationName:SKPresentationViewPageChangedNotification object:self];
 }
 
@@ -283,23 +290,53 @@
 
 #pragma mark Drawing
 
+- (NSImage *)predrawnImageAtIndex:(NSUInteger)pageIndex { return nil; }
+
+- (void)removePredrawnImageAtIndex:(NSUInteger)pageIndex {}
+
+- (NSImage *)imageWithImageRep:(NSBitmapImageRep *)imageRep page:(PDFPage 
*)aPage autoScales:(BOOL)autoScales {
+    NSRect bounds = {NSZeroPoint, [imageRep size]};
+    NSRect pageRect = [aPage boundsForBox:kPDFDisplayBoxCropBox];
+    if (([page 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);
+    [aPage drawWithBox:kPDFDisplayBoxCropBox toContext:context];
+    CGContextRestoreGState(context);
+    
+    NSImage *image = [[NSImage alloc] initWithSize:bounds.size];
+    [image addRepresentation:imageRep];
+    
+    return image;
+}
+
 - (void)displayPage:(void (^)(void))completionHandler {
-    if (page == nil) {
-        [pageLayer setContents:nil];
+    if (page == nil)
+        return;
+    
+    NSUInteger pageIndex = [page pageIndex];
+    NSImage *predrawnImage = [self predrawnImageAtIndex:pageIndex];
+    
+    if (predrawnImage) {
+        [pageLayer setContents:predrawnImage];
+        [self removePredrawnImageAtIndex:pageIndex];
         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);
-    }
+    NSBitmapImageRep *imageRep = [self 
bitmapImageRepForCachingDisplayInRect:[self bounds]];
     
-    NSRect bounds = [self bounds];
-    NSBitmapImageRep *imageRep = [self 
bitmapImageRepForCachingDisplayInRect:bounds];
-    
     if (imageRep == nil) {
         if (completionHandler)
             completionHandler();
@@ -306,31 +343,19 @@
         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.drawing", 
queuePriority);
+    }
+    
     PDFPage *thePage = page;
-    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)));
-
+    BOOL autoScales = [self autoScales];
+    
     dispatch_async(drawingQueue, ^{
         
-        CGContextRef context = [[NSGraphicsContext 
graphicsContextWithBitmapImageRep:imageRep] CGContext];
+        NSImage *image = [self imageWithImageRep:imageRep page:thePage 
autoScales:autoScales];
         
-        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)
@@ -341,6 +366,7 @@
         });
         
     });
+    
 }
 
 - (NSBitmapImageRep *)bitmapImageRepCachingDisplay {
@@ -396,18 +422,95 @@
     return self;
 }
 
+- (void)viewWillStartLiveResize {
+    [super viewWillStartLiveResize];
+    [self removePredrawnImageAtIndex:NSNotFound];
+}
+
 - (void)viewDidEndLiveResize {
     [super viewDidEndLiveResize];
+    [self removePredrawnImageAtIndex:NSNotFound];
     [self displayPage:nil];
 }
 
 - (void)updatedAnnotationOnPage:(PDFPage *)aPage {
     if (page == aPage) {
+        [self removePredrawnImageAtIndex:[aPage pageIndex]];
         [[self class] cancelPreviousPerformRequestsWithTarget:self 
selector:@selector(displayPage:) object:nil];
         [self performSelector:@selector(displayPage:) withObject:nil 
afterDelay:0.0];
     }
 }
 
+- (NSImage *)predrawnImageAtIndex:(NSUInteger)pageIndex {
+    if (predrawnImages == nil)
+        return nil;
+    NSImage *image = (__bridge id)NSMapGet(predrawnImages, (void *)pageIndex);
+    if ([image isKindOfClass:[NSImage class]])
+        return image;
+    return nil;
+}
+
+- (void)removePredrawnImageAtIndex:(NSUInteger)pageIndex {
+    if (pageIndex == NSNotFound)
+        predrawnImages = nil;
+    else if (predrawnImages)
+        NSMapRemove(predrawnImages, (void *)pageIndex);
+}
+
+- (void)displayPage:(void (^)(void))completionHandler {
+    if (page == nil)
+        return;
+    
+    [super displayPage:completionHandler];
+    
+    // generate an image for the next page in the background, which is usually 
needed next for a presentation
+    
+    NSUInteger pageIndex = [page pageIndex] + 1;
+    
+    if (pageIndex >= [[page document] pageCount])
+        return;
+    
+    if (predrawnImages == nil)
+        predrawnImages = [[NSMapTable alloc] 
initWithKeyOptions:NSPointerFunctionsOpaqueMemory | 
NSPointerFunctionsIntegerPersonality 
valueOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality capacity:2];
+    else if (NSMapGet(predrawnImages, (void *)pageIndex))
+        return;
+    
+    // set NSNull so we can invalidate this image
+    NSMapInsert(predrawnImages, (void *)pageIndex, (__bridge void *)[NSNull 
null]);
+    
+    static dispatch_queue_t predrawingQueue = nil;
+    if (predrawingQueue == nil) {
+        dispatch_queue_attr_t queuePriority = 
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, 
QOS_CLASS_BACKGROUND, 0);
+        predrawingQueue = 
dispatch_queue_create("net.sourceforge.skim-app.skim.pageview.predrawing", 
queuePriority);
+    }
+    
+    NSBitmapImageRep *imageRep = [self 
bitmapImageRepForCachingDisplayInRect:[self bounds]];
+    
+    if (imageRep == nil)
+        return;
+    
+    PDFPage *thePage = [[page document] pageAtIndex:pageIndex];
+    BOOL autoScales = [self autoScales];
+    
+    dispatch_async(predrawingQueue, ^{
+        
+        NSImage *image = [self imageWithImageRep:imageRep page:thePage 
autoScales:autoScales];
+        
+        dispatch_async(dispatch_get_main_queue(), ^{
+            
+            if (image && predrawnImages && (__bridge 
id)NSMapGet(predrawnImages, (void *)pageIndex) == [NSNull null]) {
+                if (page == thePage)
+                    [pageLayer setContents:image];
+                else
+                    NSMapInsert(predrawnImages, (void *)pageIndex, (__bridge 
void *)image);
+            }
+            
+        });
+        
+    });
+    
+}
+
 #pragma mark Accessors
 
 - (BOOL)canBecomeKeyView {
@@ -422,6 +525,7 @@
     if (flag != pvFlags.autoScales) {
         pvFlags.autoScales = flag;
         [pageLayer setContentsGravity:flag ? kCAGravityResizeAspectFill : 
kCAGravityCenter];
+        [self removePredrawnImageAtIndex:NSNotFound];
         [self displayPage:nil];
         [[NSNotificationCenter defaultCenter] 
postNotificationName:SKPresentationViewAutoScalesChangedNotification 
object:self];
     }

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