surface = [[EmacsSurface alloc] initWithSize:s
ColorSpace:[[[self window] colorSpace]
- CGColorSpace]];
+ CGColorSpace]
+ Scale:scale];
/* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay
the layer's scale factor is not set automatically, so do it
now. */
- [[self layer] setContentsScale:[[self window] backingScaleFactor]];
+ [[self layer] setContentsScale:scale];
}
CGContextRef context = [surface getContext];
- CGContextTranslateCTM(context, 0, [surface getSize].height);
- CGContextScaleCTM(context, scale, -scale);
-
[NSGraphicsContext
setCurrentContext:[NSGraphicsContext
graphicsContextWithCGContext:context
NSTRACE ("[EmacsView unfocusDrawingBuffer]");
[NSGraphicsContext setCurrentContext:nil];
- [surface releaseContext];
[self setNeedsDisplay:YES];
}
There's a private method, -[CALayer setContentsChanged], that we
could use to force it, but we shouldn't often get the same
surface twice in a row. */
+ [surface releaseContext];
[[self layer] setContents:(id)[surface getSurface]];
+ [surface performSelectorOnMainThread:@selector (getContext)
+ withObject:nil
+ waitUntilDone:NO];
}
#endif
probably be some sort of pruning job that removes excess
surfaces. */
+#define CACHE_MAX_SIZE 2
- (id) initWithSize: (NSSize)s
ColorSpace: (CGColorSpaceRef)cs
+ Scale: (CGFloat)scl
{
NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]");
[super init];
- cache = [[NSMutableArray arrayWithCapacity:3] retain];
+ cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain];
size = s;
colorSpace = cs;
+ scale = scl;
return self;
}
if (currentSurface)
CFRelease (currentSurface);
- if (lastSurface)
- CFRelease (lastSurface);
for (id object in cache)
CFRelease ((IOSurfaceRef)object);
calls cannot be nested. */
- (CGContextRef) getContext
{
- IOSurfaceRef surface = NULL;
-
- NSTRACE ("[EmacsSurface getContextWithSize:]");
- NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0));
+ NSTRACE ("[EmacsSurface getContext]");
- for (id object in cache)
+ if (!context)
{
- if (!IOSurfaceIsInUse ((IOSurfaceRef)object))
- {
- surface = (IOSurfaceRef)object;
- [cache removeObject:object];
- break;
- }
- }
+ IOSurfaceRef surface = NULL;
- if (!surface)
- {
- int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
- size.width * 4);
+ NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0));
- surface = IOSurfaceCreate
- ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width],
- (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
- (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
- (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
- (id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']});
- }
+ for (id object in cache)
+ {
+ if (!IOSurfaceIsInUse ((IOSurfaceRef)object))
+ {
+ surface = (IOSurfaceRef)object;
+ [cache removeObject:object];
+ break;
+ }
+ }
- IOReturn lockStatus = IOSurfaceLock (surface, 0, nil);
- if (lockStatus != kIOReturnSuccess)
- NSLog (@"Failed to lock surface: %x", lockStatus);
+ if (!surface && [cache count] >= CACHE_MAX_SIZE)
+ {
+ /* Just grab the first one off the cache. This may result
+ in tearing effects. The alternative is to wait for one
+ of the surfaces to become free. */
+ surface = (IOSurfaceRef)[cache firstObject];
+ [cache removeObject:(id)surface];
+ }
+ else if (!surface)
+ {
+ int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
+ size.width * 4);
+
+ surface = IOSurfaceCreate
+ ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width],
+ (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
+ (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
+ (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
+ (id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']});
+ }
+
+ IOReturn lockStatus = IOSurfaceLock (surface, 0, nil);
+ if (lockStatus != kIOReturnSuccess)
+ NSLog (@"Failed to lock surface: %x", lockStatus);
- [self copyContentsTo:surface];
+ [self copyContentsTo:surface];
- currentSurface = surface;
+ currentSurface = surface;
+
+ context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface),
+ IOSurfaceGetWidth (currentSurface),
+ IOSurfaceGetHeight (currentSurface),
+ 8,
+ IOSurfaceGetBytesPerRow (currentSurface),
+ colorSpace,
+ (kCGImageAlphaPremultipliedFirst
+ | kCGBitmapByteOrder32Host));
+
+ CGContextTranslateCTM(context, 0, size.height);
+ CGContextScaleCTM(context, scale, -scale);
+ }
- context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface),
- IOSurfaceGetWidth (currentSurface),
- IOSurfaceGetHeight (currentSurface),
- 8,
- IOSurfaceGetBytesPerRow (currentSurface),
- colorSpace,
- (kCGImageAlphaPremultipliedFirst
- | kCGBitmapByteOrder32Host));
return context;
}
{
NSTRACE ("[EmacsSurface releaseContextAndGetSurface]");
+ if (!context)
+ return;
+
CGContextRelease (context);
context = NULL;
if (lockStatus != kIOReturnSuccess)
NSLog (@"Failed to unlock surface: %x", lockStatus);
- /* Put lastSurface back on the end of the cache. It may not have
- been displayed on the screen yet, but we probably want the new
- data and not some stale data anyway. */
- if (lastSurface)
- [cache addObject:(id)lastSurface];
+ /* Put currentSurface back on the end of the cache. */
+ [cache addObject:(id)currentSurface];
lastSurface = currentSurface;
currentSurface = NULL;
}
NSTRACE ("[EmacsSurface copyContentsTo:]");
- if (! lastSurface)
+ if (!lastSurface || lastSurface == destination)
return;
lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil);
NSLog (@"Failed to unlock source surface: %x", lockStatus);
}
+#undef CACHE_MAX_SIZE
@end /* EmacsSurface */