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
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit