Swift中如何裁剪主体外透明像素并填充主体内部为黑色?
问题描述
我有一张PNG蒙版图:
希望将主体内部填充为黑色。初步思路是裁剪掉主体外的透明像素,再添加纯色背景,也想知道其他可行方法。
目前已实现主体轮廓绘制,代码如下:
Image(uiImage: UIImage(named: "outline")!.stroked()) .resizable() .aspectRatio(contentMode: .fit) .background(.red)
当前能得到精准的主体轮廓:
但最终需要的效果是:
解决方案
方法一:路径填充(推荐)
直接基于蒙版的轮廓路径填充内部,无需额外裁剪操作,步骤如下:
1. 扩展UIImage实现填充+描边
在现有stroked()方法基础上,添加填充逻辑:
extension UIImage { func filledAndStroked(fillColor: UIColor = .black, strokeColor: UIColor = .black, strokeWidth: CGFloat = 1) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, scale) defer { UIGraphicsEndImageContext() } guard let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return nil } let rect = CGRect(origin: .zero, size: size) // 绘制原图以提取路径 context.draw(cgImage, in: rect) guard let maskPath = context.path else { return nil } // 填充内部区域 fillColor.setFill() context.addPath(maskPath) context.fillPath() // 绘制轮廓 strokeColor.setStroke() context.setLineWidth(strokeWidth) context.addPath(maskPath) context.strokePath() return UIGraphicsGetImageFromCurrentImageContext() } }
2. SwiftUI中调用
Image(uiImage: UIImage(named: "outline")!.filledAndStroked()) .resizable() .aspectRatio(contentMode: .fit)
方法二:裁剪+背景填充
如果坚持用你最初的思路,可按以下步骤实现:
1. 计算蒙版的有效区域(裁剪透明部分)
extension UIImage { func cropTransparentArea() -> UIImage? { guard let cgImage = cgImage else { return nil } let alphaInfo = cgImage.alphaInfo guard alphaInfo == .first || alphaInfo == .last || alphaInfo == .premultipliedFirst || alphaInfo == .premultipliedLast else { return self } let width = cgImage.width let height = cgImage.height let bytesPerPixel = 4 let bytesPerRow = bytesPerPixel * width let pixelData = UnsafeMutablePointer<UInt8>.allocate(capacity: bytesPerRow * height) defer { pixelData.deallocate() } let colorSpace = CGColorSpaceCreateDeviceRGB() let context = CGContext(data: pixelData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) var minX = width, minY = height, maxX = 0, maxY = 0 for y in 0..<height { for x in 0..<width { let pixelIndex = (y * bytesPerRow) + (x * bytesPerPixel) let alpha = pixelData[pixelIndex + 3] if alpha > 0 { if x < minX { minX = x } if x > maxX { maxX = x } if y < minY { minY = y } if y > maxY { maxY = y } } } } if minX > maxX || minY > maxY { return nil } let cropRect = CGRect(x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1) guard let croppedCGImage = cgImage.cropping(to: cropRect) else { return nil } return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation) } }
2. 添加黑色背景并合成
extension UIImage { func addSolidBackground(color: UIColor = .black) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, true, scale) color.setFill() UIRectFill(CGRect(origin: .zero, size: size)) draw(at: .zero) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result } }
3. SwiftUI中组合调用
if let croppedImage = UIImage(named: "outline")?.cropTransparentArea(), let filledImage = croppedImage.addSolidBackground() { Image(uiImage: filledImage) .resizable() .aspectRatio(contentMode: .fit) }
内容的提问来源于stack exchange,提问作者erotsppa
相关产品推荐
相关产品推荐

