MacOS Swift中为NSBitmapImageRep右侧拼接PNG图像的问题
在macOS命令行程序中拼接NSBitmapImageRep与PNG图像的正确实现
修复后的完整代码
private func applyOverlay(existingBitmap: NSBitmapImageRep) -> NSBitmapImageRep? { // 直接从文件加载PNG的NSBitmapImageRep,避免NSImage的缩放因子干扰 guard let pngData = try? Data(contentsOf: URL(fileURLWithPath: mOverlayPath!)), let pngImgRep = NSBitmapImageRep(data: pngData) else { print("Failed to open overlay file: \(mOverlayPath!)") return nil } let pngPixelWidth = pngImgRep.pixelsWide let pngPixelHeight = pngImgRep.pixelsHigh let originalPixelWidth = existingBitmap.pixelsWide let originalPixelHeight = existingBitmap.pixelsHigh // 计算新位图尺寸:原宽度 + PNG宽度 + 右侧边距 let newPixelWidth = originalPixelWidth + pngPixelWidth + TEXT_MARGIN_RIGHT let newPixelHeight = originalPixelHeight // 创建新位图,复用原图的色彩空间、位深等参数,避免格式不兼容 guard let combinedImgRep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: newPixelWidth, pixelsHigh: newPixelHeight, bitsPerSample: existingBitmap.bitsPerSample, samplesPerPixel: existingBitmap.samplesPerPixel, hasAlpha: existingBitmap.hasAlpha, isPlanar: existingBitmap.isPlanar, colorSpaceName: existingBitmap.colorSpaceName, bytesPerRow: 0, bitsPerPixel: 0 ) else { print("Failed to create combined bitmap image rep") return nil } // 填充白色背景到整个新位图 fillBackground(bitmap: combinedImgRep, color: .white) // 复制原图像到新位图左侧 copyPixelData(dest: combinedImgRep, src: existingBitmap, destX: 0, destY: 0) // 计算PNG垂直居中的Y坐标,复制到指定位置 let dstY = (originalPixelHeight - pngPixelHeight) / 2 copyPixelData(dest: combinedImgRep, src: pngImgRep, destX: originalPixelWidth, destY: dstY) return combinedImgRep } /// 给位图填充背景色 private func fillBackground(bitmap: NSBitmapImageRep, color: NSColor) { NSGraphicsContext.saveGraphicsState() defer { NSGraphicsContext.restoreGraphicsState() } let context = NSGraphicsContext(bitmapImageRep: bitmap)! NSGraphicsContext.current = context color.setFill() NSRect(x: 0, y: 0, width: bitmap.pixelsWide, height: bitmap.pixelsHigh).fill() } /// 直接复制像素数据,彻底避免绘制缩放问题 private func copyPixelData(dest: NSBitmapImageRep, src: NSBitmapImageRep, destX: Int, destY: Int) { guard let destData = dest.bitmapData, let srcData = src.bitmapData else { return } let srcBytesPerRow = src.bytesPerRow let destBytesPerRow = dest.bytesPerRow let bytesPerPixel = src.samplesPerPixel * (src.bitsPerSample / 8) // 逐行复制像素数据 for row in 0..<src.pixelsHigh { let srcRowOffset = row * srcBytesPerRow let destRowOffset = (destY + row) * destBytesPerRow + destX * bytesPerPixel memcpy(destData + destRowOffset, srcData + srcRowOffset, srcBytesPerRow) } }
问题分析与修复点
1. PNG图像缩放异常的解决
- 错误根源:原代码通过
NSImage中转加载PNG,会自动应用系统缩放因子(Retina屏为2x),导致生成的NSBitmapImageRep像素尺寸被压缩;同时使用srcImg.size获取的是点尺寸(非像素尺寸),绘制时触发缩放。 - 修复方式:直接从文件数据加载
NSBitmapImageRep,跳过NSImage的中间转换,保留原始像素尺寸;改用直接复制像素数据的逻辑替代draw方法,彻底避免绘制缩放问题。
2. 编译报错的解决
- 错误根源:Swift中
NSCompositingOperation的枚举值为小写驼峰格式(如.copy、.sourceOver),而非Objective-C时代的NSCompositeSourceOver;原draw方法参数也存在不匹配问题。 - 修复方式:改用直接复制像素的逻辑,无需依赖
NSCompositingOperation,同时规避了绘制API的参数兼容问题。
3. 其他代码错误修复
- 修正原
copyImage函数中destHeight = destImg.pixelsWide的笔误,改为destImg.pixelsHigh,避免绘制区域错误。 - 创建新位图时复用原图的色彩空间、位深等参数,避免色彩偏差或格式不兼容。
- 新增白色背景填充逻辑,满足需求中“右侧添加白色背景像素”的要求。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

