10

Mac OS X 10.7.4

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

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

CGContextRef但是,当我使用C 函数在此图形上下文中绘图时,我看不到我的绘图结果。没有任何效果。

由于我不会进入的原因,我真的需要使用CGContextRef函数(而不是 CocoaNSBezierPath类)进行绘制。

我的代码示例如下所示。我正在尝试画一个简单的“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];
4

5 回答 5

31

您没有指定如何查看结果。我假设您正在查看NSImage img而不是NSBitmapImageRep offscreenRep.

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

与其将焦点锁定在 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
于 2012-05-16T22:35:04.250 回答
6

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

init 方法的文档

- (instancetype)initWithSize:(NSSize)aSize

自 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];

这就是所有人。

graphicsPort实际上是void *

@property (readonly) void * graphicsPort 

并记录为

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

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

在 OS X 中,这是 Core Graphics 上下文,一个CGContextRef对象(不透明类型)。

此属性已在 10.10 中弃用,取而代之的是新属性

@property (readonly) CGContextRef CGContext

仅在 10.10 及更高版本中可用。如果您必须支持旧系统,仍然可以使用graphicsPort.

于 2016-05-24T09:36:11.610 回答
6

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

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

用法:

let image = NSImage(size: CGSize(width: 100, height: 100), actions: { ctx in
    // Drawing commands here for example:
    // ctx.setFillColor(.white)
    // ctx.fill(pageRect)
})
于 2020-01-21T08:45:49.683 回答
1

这是绘制相同图像的 3 种方法(Swift 4)。

@Mecki 建议的方法可以生成没有blurring伪影(如模糊曲线)的图像。但这可以通过调整CGContext设置来解决(不包括在这个例子中)。

public struct ImageFactory {

   public static func image(size: CGSize, fillColor: NSColor, rounded: Bool = false) -> NSImage? {
      let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
      return drawImage(size: size) { context in
         if rounded {
            let radius = min(size.height, size.width)
            let path = NSBezierPath(roundedRect: rect, xRadius: 0.5 * radius, yRadius: 0.5 * radius).cgPath
            context.addPath(path)
            context.clip()
         }
         context.setFillColor(fillColor.cgColor)
         context.fill(rect)
      }
   }

}

extension ImageFactory {

   private static func drawImage(size: CGSize, drawingCalls: (CGContext) -> Void) -> NSImage? {
      return drawImageInLockedImageContext(size: size, drawingCalls: drawingCalls)
   }

   private static func drawImageInLockedImageContext(size: CGSize, drawingCalls: (CGContext) -> Void) -> NSImage? {
      let image = NSImage(size: size)
      image.lockFocus()
      guard let context = NSGraphicsContext.current else {
         image.unlockFocus()
         return nil
      }
      drawingCalls(context.cgContext)
      image.unlockFocus()
      return image
   }

   // Has scalling or antialiasing issues, like blurred curves.
   private static func drawImageInBitmapImageContext(size: CGSize, drawingCalls: (CGContext) -> Void) -> NSImage? {
      guard let offscreenRep = NSBitmapImageRep(pixelsWide: Int(size.width), pixelsHigh: Int(size.height),
                                                bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true,
                                                isPlanar: false, colorSpaceName: .deviceRGB) else {
                                                   return nil
      }
      guard let context = NSGraphicsContext(bitmapImageRep: offscreenRep) else {
         return nil
      }
      NSGraphicsContext.saveGraphicsState()
      NSGraphicsContext.current = context
      drawingCalls(context.cgContext)
      NSGraphicsContext.restoreGraphicsState()
      let img = NSImage(size: size)
      img.addRepresentation(offscreenRep)
      return img
   }

   // Has scalling or antialiasing issues, like blurred curves.
   private static func drawImageInCGContext(size: CGSize, drawingCalls: (CGContext) -> Void) -> NSImage? {
      let colorSpace = CGColorSpaceCreateDeviceRGB()
      let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
      guard let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8,
                                    bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
         return nil
      }
      drawingCalls(context)
      guard let image = context.makeImage() else {
         return nil
      }
      return NSImage(cgImage: image, size: size)
   }
}
于 2018-07-04T14:35:44.967 回答
1

Swift 4:我使用这段代码,它从 UIKit 复制了方便的 API(但在 macOS 上运行):

public class UIGraphicsImageRenderer {
    let size: CGSize

    init(size: CGSize) {
        self.size = size
    }

    func image(actions: (CGContext) -> Void) -> NSImage {
        let image = NSImage(size: size)
        image.lockFocusFlipped(true)
        actions(NSGraphicsContext.current!.cgContext)
        image.unlockFocus()
        return image
    }
}

用法:

let renderer = UIGraphicsImageRenderer(size: imageSize)
let image = renderer.image { ctx in
    // Drawing commands here
}
于 2019-02-16T00:30:38.543 回答