macOS使用NSImage绘制图像
macOS使用NSImage绘制图像

macOS使用NSImage绘制图像

NSImage 实现图像绘制的关键在于图像上下文(Graphics Context)机制,配合 NSGraphicsContext 和 NSImageRep 系列类,完成位图或矢量图的渲染。它不是简单的 bitmap 容器,而是一个“绘图容器”,可以组合多个图像表示(NSImageRep),通过绘图命令进行合成。

NSImage 绘制图像的核心机制

1、NSImage 是容器,而不是像素位图本身

NSImage 本身不直接存储像素数据,而是内部包含多个图像表示(NSImageRep 对象),比如:

NSBitmapImageRep:位图表示(像素数据);

NSPDFImageRep:PDF 矢量图;

NSCIImageRep:Core Image 图像;

NSCachedImageRep、NSCustomImageRep 等。

可以使用representations返回所有已添加的图像表示:

image.representations // 返回 [NSImageRep]

2、绘图前需要使用lockFocus() 建立上下文

lockFocus() 会把图像设为当前绘图目标,也就是建立一个图形上下文NSGraphicsContext:

image.lockFocus()   // 锁定绘图目标,开始绘图
...     // 绘图过程
image.unlockFocus() // 解锁绘图目标,完成绘图

期间的所有绘制(draw()、Core Graphics、NSBezierPath)都会“画到”这张图上。

内部实际上是调用 NSGraphicsContext.current = … 设置当前上下文。

3、调用draw() 实现绘制

例如:

otherImage.draw(at: point, from: .zero, operation: .sourceOver, fraction: 1.0)

这是向当前图形上下文(由 lockFocus() 设置)中绘制一张图像的标准方式,类似 Photoshop 合成图层。

4、unlockFocus() 提交绘图

绘制完成后调用 unlockFocus() 会:

1)恢复上下文(解除绘图绑定);

2)图像数据被写入到 NSBitmapImageRep 或类似的表示中;

如果不调用 unlockFocus(),绘制是不会生效的。

NSImage.draw方法

draw 是 NSImage 中非常常用的方法,用于把图像绘制到当前的图形上下文中(比如:窗口、视图、或另一个 NSImage 上)。

常用的三种draw写法:

1、draw(at:from:operation:fraction:)

func draw(at point: NSPoint, from rect: NSRect, operation: NSCompositingOperation, fraction: CGFloat)

作用:将图像的指定区域(from)绘制到当前位置(at)

参数说明:

1)point:绘制目标位置(左下角为原点);

2)rect:从原图中提取的区域(通常用 .zero 表示整个图);

3)operation:混合模式(如 .sourceOver);

4)fraction:透明度(0.0~1.0)。

示例:把图像绘制在 (50, 50) 位置

let image = NSImage(named: "example")!
image.draw(at: NSPoint(x: 50, y: 50),
           from: .zero,
           operation: .sourceOver,
           fraction: 1.0)

2、draw(in:)

func draw(in dstRect: NSRect)

作用:将整个图像缩放绘制到指定的矩形区域中。

适合做图像缩放(比如绘制成缩略图)。

示例:缩放绘制到 (0,0)-(100,100)

image.draw(in: NSRect(x: 0, y: 0, width: 100, height: 100))

3、draw(in:from:operation:fraction:respectFlipped:hints:)

这是最完整、底层的版本:

func draw(in dstRect: NSRect,
          from srcRect: NSRect,
          operation: NSCompositingOperation,
          fraction: CGFloat,
          respectFlipped: Bool,
          hints: [NSImageRep.HintKey : Any]?)

支持指定源区域、目标区域、混合方式、是否尊重翻转坐标、绘图提示。

一般用于复杂图像缩放或渲染优化(高性能需求)。

draw方法的注意事项

1、当前上下文:draw() 一定要在有效的绘图上下文中使用,比如 lockFocus() 之后、NSView.draw(_:) 中;

2、坐标系:默认原点在左下角,macOS 是 flipped 视图时可能在左上角;

3、高分屏:绘图时图像分辨率可能会被拉伸,应使用 image.bestRepresentation(for:context:hints:) 获取最优图像;

4、透明度:fraction 可以实现半透明绘制;

5、合成模式:.sourceOver 是最常见的,等同于 Photoshop 的“正常”图层混合。

混合模式 .sourceOver 等类型:

1).clear:清除目标区域像素;

2).copy:直接复制源像素到目标;

3).sourceOver:默认,源像素覆盖到目标上;

4).destinationOver:目标像素在源图像下;

5).multiply / .screen:类似 Photoshop 的混合模式。

代码示例

1、绘制红色圆

let image = NSImage(size: NSSize(width: 100, height: 100))
image.lockFocus()

NSColor.red.setFill()
NSBezierPath(ovalIn: NSRect(x: 10, y: 10, width: 80, height: 80)).fill()

image.unlockFocus()

2、在屏幕截图上绘制鼠标

let finalImage = NSImage(size: screenSize)  // 创建合成图片,screenSize 是屏幕尺寸
finalImage.lockFocus()

// 绘制屏幕截图,screenImage是获取的屏幕截图
screenImage.draw(at: .zero, from: .zero, operation: .sourceOver, fraction: 1.0)

// 绘制鼠标图像,cursorImage是获取的默认鼠标图像
let cursorOrigin = NSPoint(x: mouseLocation.x,y: mouseLocation.y)   // 鼠标的坐标点
cursorImage.draw(at: cursorOrigin, from: .zero, operation: .sourceOver, fraction: 1.0)

finalImage.unlockFocus()

创建屏幕大小的NSImage图像,在NSImage图像上使用屏幕截图和鼠标图像的draw方法,根据坐标点绘制图像,绘制完成后得到一个屏幕+鼠标的截图。

总结

NSImage 通过内部的图像表示和图形上下文系统,实现图像绘制和组合。可以把它想象成 Photoshop 中的一张画布,lockFocus() 后就可以自由绘制其他内容,绘完 unlockFocus(),这张图就完成了。

在 Retina 屏下,lockFocus() 使用的是像素比例为 1x 的上下文(非 HiDPI),会导致模糊。如果需要支持高分屏、图像保存、透明背景等,更推荐:

1)使用 NSGraphicsContext + NSBitmapImageRep;

2)或者直接使用 Core Graphics 或 Metal。

相关文章

MacOS图像显示类NSImage:https://fangjunyu.com/2024/11/22/macos%e5%9b%be%e5%83%8f%e6%98%be%e7%a4%ba%e7%b1%bbnsimage/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注