NSImage扩展调整大分辨率图片:设500x500却输出1000x1000
问题根源:Retina屏幕下的逻辑尺寸 vs 像素尺寸
你的问题出在NSImage的尺寸逻辑上:在高DPI(比如Retina)屏幕中,NSImage的size属性代表的是逻辑尺寸(屏幕点),而实际渲染的像素尺寸是逻辑尺寸 × 屏幕缩放因子(通常是2x)。
你当前的resizeImage方法只设置了新NSImage的逻辑尺寸为500×500,但系统默认会使用屏幕的缩放因子(2x)来生成像素数据,所以最终输出的图片像素就是500×2 × 500×2 = 1000×1000,这就是为什么你得到了双倍尺寸的结果。
修复方案:直接基于像素尺寸创建位图
要解决这个问题,我们应该绕开逻辑尺寸的干扰,直接操作NSBitmapImageRep(位图表示)来生成指定像素尺寸的图片。下面是改进后的扩展方法:
extension NSImage { func resizeToPixelSize(targetWidth: CGFloat, targetHeight: CGFloat) -> NSImage { // 直接创建指定像素尺寸的位图表示 guard let bitmapRep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: Int(targetWidth), pixelsHigh: Int(targetHeight), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSColorSpaceName.calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0 ) else { // 如果创建失败,返回原图像 return self } // 设置位图的逻辑尺寸与像素尺寸一致,避免scale干扰 bitmapRep.size = CGSize(width: targetWidth, height: targetHeight) NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep) // 启用高插值保证缩放质量 NSGraphicsContext.current?.imageInterpolation = .high // 将原图像绘制到位图的整个区域 self.draw( in: NSRect(x: 0, y: 0, width: targetWidth, height: targetHeight), from: NSRect(x: 0, y: 0, width: self.size.width, height: self.size.height), operation: .copy, fraction: 1.0 ) NSGraphicsContext.restoreGraphicsState() // 创建新的NSImage并添加位图表示 let resizedImage = NSImage(size: CGSize(width: targetWidth, height: targetHeight)) resizedImage.addRepresentation(bitmapRep) return resizedImage } }
额外优化建议
- 统一尺寸参数命名:把resize方法的参数改成更清晰的名字(比如
targetWidth/targetHeight),避免混淆逻辑尺寸和像素尺寸。 - 简化保存函数:你当前的
saveimage函数有大量重复代码,可以重构简化:func saveImage(_ image: NSImage, to urlString: String, format: String) -> Bool { guard let tiffData = image.tiffRepresentation, let bitmapRep = NSBitmapImageRep(data: tiffData) else { return false } let fileType: NSBitmapImageRep.FileType let fileExtension: String switch format.lowercased() { case ".png", "png": fileType = .png fileExtension = ".png" case ".jpg", ".jpeg", "jpg", "jpeg": fileType = .jpeg fileExtension = ".jpg" case ".tif", ".tiff", "tif", "tiff": fileType = .tiff fileExtension = ".tif" case ".bmp", "bmp": fileType = .bmp fileExtension = ".bmp" case ".gif", "gif": fileType = .gif fileExtension = ".gif" default: return false } let saveURL = URL(fileURLWithPath: urlString + fileExtension) let properties: [NSBitmapImageRep.PropertyKey: Any] = [.compressionFactor: 1.0] guard let imageData = bitmapRep.representation(using: fileType, properties: properties) else { return false } do { try imageData.write(to: saveURL) return true } catch { print("保存失败:\(error.localizedDescription)") return false } } - 验证尺寸:在保存前可以打印位图的像素尺寸,确认是否符合预期:
if let bitmapRep = image.representations.first as? NSBitmapImageRep { print("输出像素尺寸:\(bitmapRep.pixelsWide)×\(bitmapRep.pixelsHigh)") }
内容的提问来源于stack exchange,提问作者techno
相关产品推荐
相关产品推荐

