Swift中NSImage白色像素转透明的优化方案咨询
针对NSImage的白色像素透明化及相关问题解决方案
1. 更优雅地去除NSImage的Alpha通道
你当前用JPEG转码的方式既低效又可能损失图像质量,直接通过Core Graphics创建不带Alpha通道的位图上下文绘制原图像,是更优的方案:
extension NSImage { func removeAlphaChannel() -> NSImage? { guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return nil } let width = cgImage.width let height = cgImage.height let colorSpace = CGColorSpaceCreateDeviceRGB() // 创建无Alpha通道的位图上下文(bytesPerPixel=3,无透明通道) guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 3, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue) else { return nil } // 将原图像绘制到上下文 context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) guard let newCGImage = context.makeImage() else { return nil } return NSImage(cgImage: newCGImage, size: self.size) } }
调用removeAlphaChannel()即可直接得到无Alpha通道的NSImage,无需经过格式转码。
2. 仅裁剪图像边缘的白色像素
要实现只裁剪边缘白色像素,需先定位图像内非白色像素的边界,再裁剪出包含所有有效像素的最小矩形:
extension NSImage { func cropEdgeWhitePixels(tolerance: CGFloat = 4) -> NSImage? { guard let bitmapRep = NSBitmapImageRep(data: self.tiffRepresentation!) else { return nil } let width = bitmapRep.pixelsWide let height = bitmapRep.pixelsHigh let toleranceValue = Int(tolerance) var minX = width var maxX = 0 var minY = height var maxY = 0 // 遍历像素,确定有效区域边界 for y in 0..<height { for x in 0..<width { guard let pixel = bitmapRep.colorAt(x: x, y: y) else { continue } let ciColor = CIColor(color: pixel) // 判断是否为白色(带容差) let isWhite = abs(ciColor.red - 1.0) * 255 < toleranceValue && abs(ciColor.green - 1.0) * 255 < toleranceValue && abs(ciColor.blue - 1.0) * 255 < toleranceValue if !isWhite { minX = min(minX, x) maxX = max(maxX, x) minY = min(minY, y) maxY = max(maxY, y) } } } // 全为白色时返回原图 if minX > maxX || minY > maxY { return self.copy() as? NSImage } // 计算并裁剪有效区域 let cropRect = CGRect(x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1) guard let croppedCGImage = bitmapRep.cgImage(forProposedRect: &cropRect, context: nil, hints: nil) else { return nil } return NSImage(cgImage: croppedCGImage, size: NSSize(width: cropRect.width, height: cropRect.height)) } }
该方法只会裁剪图像边缘的白色区域,不会影响图像内部的白色像素。
优化后的完整mask方法
结合上述Alpha通道去除方案,优化你原有的mask方法:
extension NSImage { func mask(color: NSColor, tolerance: CGFloat = 4) -> NSImage { guard let noAlphaImage = self.removeAlphaChannel(), let data = noAlphaImage.tiffRepresentation, let rep = NSBitmapImageRep(data: data) else { return self } guard let ciColor = CIColor(color: color) else { return self } let maskComponents: [CGFloat] = [ciColor.red, ciColor.green, ciColor.blue].flatMap { value in [(value * 255) - tolerance, (value * 255) + tolerance] } guard let maskedCGImage = rep.cgImage(forProposedRect: nil, context: nil, hints: nil)?.copy(maskingColorComponents: maskComponents) else { return self } return NSImage(cgImage: maskedCGImage, size: self.size) } }
内容的提问来源于stack exchange,提问作者mdbraber
相关产品推荐
相关产品推荐

