PAYATHON 2026

如何关闭 NSImage 的 HDR 绘制并改善缩放性能?

数据小许

结论

代码已通过 kCGImageSourceDecodeToSDR 请求 SDR 解码,但小窗口缩放时的高 CPU 使用率仍然存在。现有信息不足以判断卡顿是否由 NSImage 的 HDR 绘制造成,也无法确定是否有适用于此场景的 NSImage HDR 开关。

可以先试着把图片预先绘制成指定尺寸的 8 位 sRGB 位图并缓存。拖动窗口时复用缓存,避免每次调用 draw(_:) 都缩放和绘制原始图像。

先确认卡顿发生在哪里

视图每次绘制时都会执行 image.draw(in: drawRect)。窗口缩放会触发多次重绘,因此需要查明耗时主要来自解码、色调映射、缩放还是绘制。问题中提到两组 Time Profiler 记录,但没有提供采样栈或耗时数据;FB22297294 只是反馈编号,无法据此判断具体原因。

调整窗口大小时,可以用 Time Profiler 对比以下三种情况:

  • 直接绘制原始 NSImage;
  • 直接绘制 SDR 解码得到的 CGImage;
  • 先生成目标尺寸的位图,再绘制该位图。

如果第三种方式明显降低了拖动时的绘制开销,可以采用预渲染和缓存。如果没有改善,则需要根据采样栈继续定位。

预渲染并缓存目标尺寸图像

下面的辅助函数会把源 CGImage 绘制到 8 位 sRGB 位图中。它不能保证消除所有 HDR 处理开销,但能把转换和缩放放在生成缓存时完成,避免每次视图重绘都重复处理。

import AppKit
import CoreGraphics

func makeSDRBitmap(from image: CGImage, pixelWidth: Int, pixelHeight: Int) -> CGImage? {
    guard pixelWidth > 0, pixelHeight > 0,
          let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),
          let context = CGContext(
              data: nil,
              width: pixelWidth,
              height: pixelHeight,
              bitsPerComponent: 8,
              bytesPerRow: 0,
              space: colorSpace,
              bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
          ) else {
        return nil
    }

    context.interpolationQuality = .high
    context.draw(
        image,
        in: CGRect(x: 0, y: 0, width: pixelWidth, height: pixelHeight)
    )
    return context.makeImage()
}

视图尺寸或显示器缩放比例变化时,按目标像素尺寸生成缓存图像;在 draw(_:) 中只绘制缓存结果,避免每次调用时重新创建位图。若拖动过程中尺寸变化频繁,可以按尺寸区间缓存,把相近尺寸归入同一档,减少重复生成。

缓存键至少要包含目标像素宽高和显示比例。窗口尺寸、图片内容或显示比例变化后,需要更新相应缓存。小尺寸窗口可以使用较小的缓存图,避免反复缩放完整分辨率的源图。

保留当前 SDR 解码请求并进行对比

当前代码通过 ImageIO 请求 SDR 解码,再用结果创建 NSImage。可以保留这一步作对照,分别测量解码和后续绘制的耗时。如果继续使用这条路径,生成位图缓存时应使用解码得到的 CGImage,并将结果与直接绘制该 CGImage 的情况比较。

现有材料没有 profiler 采样栈、图片格式或目标 macOS 版本,因此无法断定 kCGImageSourceDecodeToSDR 在此案例中具体执行了哪些处理,也无法保证它会降低调整大小期间的 CPU 使用率。

注意事项

  • 预渲染会增加内存占用;应限制缓存的尺寸或数量,并在图片更换时释放旧缓存。
  • 8 位 sRGB 位图会限制输出的动态范围。如果应用需要保留 HDR 高光,这种处理可能不符合显示要求。
  • 如果预渲染后的缓存仍然卡顿,应根据 Time Profiler 中的实际热点继续排查,不能只凭图片是 HDR 格式就认定根因。

备注:内容仅供参考。