Mac OS X:使用CGContextRef绘制到屏幕外的NSGraphicsContext C函数无效.为什么?

Tod*_*orf 6 macos cocoa core-graphics objective-c quartz-2d

Mac OS X 10.7.4

我正在绘制通过创建的屏幕外图形上下文+[NSGraphicsContext graphicsContextWithBitmapImageRep:].

当我使用NSBezierPath绘制到这个图形上下文时,一切都按预期工作.

但是,当我使用CGContextRefC函数绘制到这个图形上下文时,我看不到我的绘图结果.什么都行不通.

由于我不打算进入的原因,我真的需要使用CGContextRef函数(而不是Cocoa NSBezierPath类).

我的代码示例如下所示.我试图画一个简单的"X".一次使用NSBezierPath,一次使用CGContextRefC函数.第一笔中风起作用,第二笔中风不起作用.我究竟做错了什么?

NSRect imgRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);
NSSize imgSize = imgRect.size;

NSBitmapImageRep *offscreenRep = [[[NSBitmapImageRep alloc]
   initWithBitmapDataPlanes:NULL
   pixelsWide:imgSize.width
   pixelsHigh:imgSize.height
   bitsPerSample:8
   samplesPerPixel:4
   hasAlpha:YES
   isPlanar:NO
   colorSpaceName:NSDeviceRGBColorSpace
   bitmapFormat:NSAlphaFirstBitmapFormat
   bytesPerRow:0
   bitsPerPixel:0] autorelease];

// set offscreen context
NSGraphicsContext *g = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
[NSGraphicsContext setCurrentContext:g];

NSImage *img = [[[NSImage alloc] initWithSize:imgSize] autorelease];

CGContextRef ctx = [g graphicsPort];

// lock and draw
[img lockFocus];

// draw first stroke with Cocoa. this works!
NSPoint p1 = NSMakePoint(NSMaxX(imgRect), NSMinY(imgRect));
NSPoint p2 = NSMakePoint(NSMinX(imgRect), NSMaxY(imgRect));
[NSBezierPath strokeLineFromPoint:p1 toPoint:p2];

// draw second stroke with Core Graphics. This doesn't work!
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 0.0, 0.0);
CGContextAddLineToPoint(ctx, imgSize.width, imgSize.height);
CGContextClosePath(ctx);
CGContextStrokePath(ctx);

[img unlockFocus];
Run Code Online (Sandbox Code Playgroud)

Kur*_*vis 26

您没有指定查看结果的方式.我假设你正在看NSImage img而不是NSBitmapImageRep offscreenRep.

当您调用时[img lockFocus],您将当前更改为NSGraphicsContext要绘制的上下文img.所以,NSBezierPath绘图进入img,这就是你所看到的.CG绘图进入offscreenRep你没有看到的.

不是将焦点锁定到NSImage并绘制到NSImage中,而是创建一个NSImage并将offscreenRep添加为其代表之一.

NSRect imgRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);
NSSize imgSize = imgRect.size;

NSBitmapImageRep *offscreenRep = [[[NSBitmapImageRep alloc]
   initWithBitmapDataPlanes:NULL
   pixelsWide:imgSize.width
   pixelsHigh:imgSize.height
   bitsPerSample:8
   samplesPerPixel:4
   hasAlpha:YES
   isPlanar:NO
   colorSpaceName:NSDeviceRGBColorSpace
   bitmapFormat:NSAlphaFirstBitmapFormat
   bytesPerRow:0
   bitsPerPixel:0] autorelease];

// set offscreen context
NSGraphicsContext *g = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:g];

// draw first stroke with Cocoa
NSPoint p1 = NSMakePoint(NSMaxX(imgRect), NSMinY(imgRect));
NSPoint p2 = NSMakePoint(NSMinX(imgRect), NSMaxY(imgRect));
[NSBezierPath strokeLineFromPoint:p1 toPoint:p2];

// draw second stroke with Core Graphics
CGContextRef ctx = [g graphicsPort];    
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 0.0, 0.0);
CGContextAddLineToPoint(ctx, imgSize.width, imgSize.height);
CGContextClosePath(ctx);
CGContextStrokePath(ctx);

// done drawing, so set the current context back to what it was
[NSGraphicsContext restoreGraphicsState];

// create an NSImage and add the rep to it    
NSImage *img = [[[NSImage alloc] initWithSize:imgSize] autorelease];
[img addRepresentation:offscreenRep];

// then go on to save or view the NSImage
Run Code Online (Sandbox Code Playgroud)


Mec*_*cki 6

我想知道为什么每个人都写这么复杂的代码来绘制图像.除非你关心图像的确切位图表示(通常你不关心!),否则不需要创建一个.您可以创建一个空白图像并直接绘制到它.在这种情况下,系统将创建适当的位图表示(或者可能是PDF表示或系统认为更适合绘图的任何内容).

init方法的文档

- (instancetype)initWithSize:(NSSize)aSize
Run Code Online (Sandbox Code Playgroud)

从MacOS 10.0开始存在但仍未被弃用,清楚地说:

使用此方法初始化图像对象后,您需要在尝试绘制图像之前提供图像内容.您可以将焦点锁定在图像上并绘制到图像,也可以显式添加您创建的图像表示.

所以这就是我编写代码的方式:

NSRect imgRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);
NSImage * image = [[NSImage alloc] initWithSize:imgRect.size];

[image lockFocus];
// draw first stroke with Cocoa
NSPoint p1 = NSMakePoint(NSMaxX(imgRect), NSMinY(imgRect));
NSPoint p2 = NSMakePoint(NSMinX(imgRect), NSMaxY(imgRect));
[NSBezierPath strokeLineFromPoint:p1 toPoint:p2];

// draw second stroke with Core Graphics
CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 0.0, 0.0);
CGContextAddLineToPoint(ctx, imgRect.size.width, imgRect.size.height);
CGContextClosePath(ctx);
CGContextStrokePath(ctx);
[image unlockFocus];
Run Code Online (Sandbox Code Playgroud)

这就是所有人.

graphicsPort实际上是void *:

@property (readonly) void * graphicsPort 
Run Code Online (Sandbox Code Playgroud)

并记录为

由图形端口表示的低级,特定于平台的图形上下文.

这可能是一切,但最后的说明

在OS X中,这是Core Graphics上下文,一个CGContextRef对象(opaque类型).

此属性已在10.10中弃用,以支持新属性

@property (readonly) CGContextRef CGContext
Run Code Online (Sandbox Code Playgroud)

仅适用于10.10及更高版本.如果你必须支持旧系统,那么仍然可以使用graphicsPort.


GOR*_*GOR 5

@Robin Stewart 的解决方案对我来说效果很好。我能够将其压缩为 NSImage 扩展名。

extension NSImage {
    convenience init(size: CGSize, actions: (CGContext) -> Void) {
        self.init(size: size)
        lockFocusFlipped(false)
        actions(NSGraphicsContext.current!.cgContext)
        unlockFocus()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let image = NSImage(size: CGSize(width: 100, height: 100), actions: { ctx in
    // Drawing commands here for example:
    // ctx.setFillColor(.white)
    // ctx.fill(pageRect)
})
Run Code Online (Sandbox Code Playgroud)