如何禁用NSImage的HDR绘制?解决窗口缩放卡顿问题
问题:iPhone来源的HDR NSImage在小窗口缩放时卡顿严重
问题现象
- 使用从iPhone获取的常规NSImage,当窗口缩放至小于100px时绘制极度卡顿,CPU占用极高
- 窗口尺寸较大时绘制正常,CPU占用处于低水平
- 已尝试的优化方案均未解决小尺寸场景下的高CPU问题:
- 使用
kCGImageSourceDecodeToSDR解码HDR图像 - 从CGImage重新创建NSImage
- 使用
- 非HDR图像在相同小尺寸缩放场景下无绘制卡顿问题
- 补充信息:已知
CGContext和NSImageView的存在,问题编号FB22297294(附视频示例、性能截图、相关代码)
可行解决方案
1. 预缩放HDR图像至常用小尺寸
提前将HDR图像解码并缩放到几个常用的小尺寸(如64px、32px等),窗口缩小时直接调用预缩放后的图像,避免实时缩放带来的高计算开销:
func preScaleHDRImage(_ image: NSImage, targetSizes: [NSSize]) -> [NSSize: NSImage] { var scaledImageMap = [NSSize: NSImage]() for size in targetSizes { let bitmapRep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0 )! bitmapRep.size = size NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep) image.draw(in: NSRect(origin: .zero, size: size)) NSGraphicsContext.current = nil let scaledImage = NSImage(size: size) scaledImage.addRepresentation(bitmapRep) scaledImageMap[size] = scaledImage } return scaledImageMap }
2. 临时禁用颜色管理绘制小尺寸图像
小尺寸下HDR图像的颜色转换开销占比极高,绘制时临时切换到设备RGB颜色空间,减少颜色管理的计算量:
func drawHDRImageWithoutColorManagement(_ image: NSImage, in rect: NSRect) { guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return } guard let context = NSGraphicsContext.current?.cgContext else { return } let originalColorSpace = context.colorSpace context.setColorSpace(CGColorSpaceCreateDeviceRGB()) context.draw(cgImage, in: rect) context.setColorSpace(originalColorSpace) }
3. 用Core Image硬件加速缩放
利用Core Image的GPU加速能力处理HDR图像缩放,比CPU端的实时缩放效率更高:
func scaleHDRImageWithCoreImage(_ image: NSImage, targetSize: NSSize) -> NSImage? { guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil), let ciImage = CIImage(cgImage: cgImage) else { return nil } let scaleX = targetSize.width / image.size.width let scaleY = targetSize.height / image.size.height let scaledCI = ciImage.transformed(by: CGAffineTransform(scaleX: scaleX, y: scaleY)) let ciContext = CIContext(options: [.useSoftwareRenderer: false]) guard let scaledCGImage = ciContext.createCGImage(scaledCI, from: scaledCI.extent) else { return nil } return NSImage(cgImage: scaledCGImage, size: targetSize) }
内容的提问来源于stack exchange,提问作者Marek H
相关产品推荐
相关产品推荐

