NSImage
にはサイズとピクセルサイズの概念がある(これはRetinaディスプレイ5Kディスプレイへの対応のため)が,NSImage
に対してそのままアフィン変換を適用したら,画像のピクセルサイズがおかしくなってしまった.これを避ける方法を色々試した結果,一旦CIFilter
を咬ませる方法が良さそうだとわかった.
フィルタのソース
extension NSImage {
public var ciImage: CIImage? {
guard let imageData = self.tiffRepresentation else { return nil }
return CIImage(data: imageData)
}
public var flipped: NSImage {
guard let flipFilter = CIFilter(name: "CIAffineTransform") else { return self }
flipFilter.setValue(self.ciImage, forKey: kCIInputImageKey)
flipFilter.setValue(CGAffineTransform(a: -1, b: 0, c: 0, d: 1, tx: self.size.width, ty: 0), forKey: kCIInputTransformKey)
guard let output = flipFilter.outputImage else { return self }
let rep = NSCIImageRep(ciImage: output)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}
public var mono: NSImage {
guard let monoFilter = CIFilter(name: "CIPhotoEffectMono") else { return self }
monoFilter.setValue(self.ciImage, forKey: kCIInputImageKey)
guard let output = monoFilter.outputImage else { return self }
let rep = NSCIImageRep(ciImage: output)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}
public var inverted: NSImage {
guard let invertFilter = CIFilter(name: "CIColorInvert") else { return self }
invertFilter.setValue(self.ciImage, forKey: kCIInputImageKey)
guard let output = invertFilter.outputImage else { return self }
let rep = NSCIImageRep(ciImage: output)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}
}
使用例
let original = NSImage(imageLiteralResourceName: "Sample")
let flippedImage = original.flipped
let monoImage = original.mono
let invertedImage = original.inverted