在NSImage上以45度角平铺绘制Logo时左侧出现空白间距问题
NSImage 45度斜向平铺Logo左侧空白问题解决
问题根源
- 坐标变换逻辑冲突:调用
context.translateBy(x:y:)移动上下文原点后,绘制Logo时又传入了绝对坐标(x,y),导致坐标双重偏移,图案实际绘制位置和预期不符 - 平铺覆盖范围不足:Logo旋转45度后外接矩形尺寸大于原图,仅从画布原点(0,0)开始循环平铺,会遗漏画布左上方延伸区域的图案,直接导致左侧出现空白;原有步长计算仅取Logo宽度,旋转后会出现图案重叠或留缝问题
- CIImage方案逻辑错误:NSImage的
.ciImage属性仅对由CIImage初始化的实例生效,普通方式加载的NSImage该属性默认值为nil,会导致判断分支不执行,无法完成绘制
修正后的CoreGraphics原生实现
let angle = -CGFloat.pi / 4 // 逆时针旋转45度 let logoSize = logo.size let sinVal = sin(abs(angle)) let cosVal = cos(abs(angle)) // 计算旋转后平铺的横纵步长,保证图案无重叠无空隙 let stepX = logoSize.width * cosVal + logoSize.height * sinVal let stepY = logoSize.width * sinVal + logoSize.height * cosVal // 向外延伸足够的偏移量,覆盖旋转后超出画布边缘的图案区域 let extendOffset = max(logoSize.width, logoSize.height) * 2 context.saveGState() // 循环范围从负偏移开始,到画布尺寸加正偏移结束,完全覆盖所有需要绘制的区域 var x = -extendOffset while x < size.width + extendOffset { var y = -extendOffset while y < size.height + extendOffset { context.saveGState() context.translateBy(x: x, y: y) context.rotate(by: angle) // 坐标系已完成变换,绘制位置直接传.zero即可,无需重复传入x/y坐标 logo.draw(at: .zero, from: .zero, operation: .sourceOver, fraction: logotransparency) context.restoreGState() y += stepY } x += stepX } context.restoreGState()
修正后的CIImage平铺实现(逻辑更简洁)
如果要使用CIImage自带的平铺能力,需要手动完成NSImage到CIImage的转换,不能直接读取.ciImage属性:
let dirtyRect = NSMakeRect(0, 0, size.width, size.height) let deg45 = CGFloat.pi / 4 // 手动从NSImage获取CGImage再转换为CIImage guard let cgImage = logo.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return } let ciImage = CIImage(cgImage: cgImage) // 调用自定义tiled方法完成45度旋转平铺后裁剪到画布尺寸 let ciTiled = ciImage.tiled(at: deg45).cropped(to: dirtyRect) // 将CIImage转回NSImage生成图案颜色 let rep = NSCIImageRep(ciImage: ciTiled) let tiledPattern = NSImage(size: dirtyRect.size) tiledPattern.addRepresentation(rep) let fillColor = NSColor(patternImage: tiledPattern) fillColor.setFill() context.fill(dirtyRect)
内容的提问来源于stack exchange,提问作者techno
相关产品推荐
相关产品推荐

