🍁
Swift: 画像をプログラムで描画するときのTips
Retinaディスプレイ環境時、画像をプログラムで生成するとき気をつけないと、なぜか指定したSizeの2倍のピクセルサイズの画像が生成されます。リサイズとかトリミングをする時にこれはかなり厄介なので、知見を書き残しておきます。
大丈夫なパターン
// 1 コンテンツのURL/パスから直接生成
let image = NSImage(contentsOf: URL)
// 2 NSBitmapImageRepを使う
guard let data = try? Data(contentsOf: URL),
let rep = NSBitmapImageRep(data: data),
let pngData = rep.representation(using: .png, properties: [:])
let image = NSImage(data: pngData) else {
return
}
// 3 CIImageをいったん経由する
let cgImage = CGImage() // 何かしらCGImageを生成
let ciImage = CIImage(cgImage: cgImage),
let rep = NSCIImageRep(ciImage: ciImage)
let image = NSimage(size: rep.size)
image.addRepresentation(rep)
// 4 CGContextを使う
let size = CGSize() // 目的のピクセルサイズ
guard let cgContext = CGContext(data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: 8,
bytesPerRow: 4 * Int(size.width),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
return
}
// 塗りつぶしたり図形描いたりする場合(色々ある)
cgContext.setFillColor(CGColor.white)
cgContext.fill(CGRect(origin: .zero, size: size))
cgContext.setLineColor(CGColor.black)
cgContext.stroke(CGRect(origin: .zero, size: size))
// 画像からやる場合
let cgImage = CGImage() 何かしらCGImageを生成
cgContext.draw(cgImage, in: CGRect(origin: .zero, size: size))
guard let image = cgContext.makeImage() else { return }
ダメなパターン
// 1 NSImage(cgImage:size:)を使う
let cgImage = CGImage() // 何かしらCGImageを生成
let image = NSImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
// 2 NSImage(size:), NSImage.lockFocus()/unlockFocus(), NSGraphicsContextを使う
let originalImage = NSImage() // ピクセルサイズがちゃんとしている画像
let rect = CGRect() // 目的の画像サイズの矩形
let image = NSImage(size: rect.size)
image.lockFocus()
if let ctx = NSGraphicsContext.current {
NSColor.white.setFill()
rect.fill()
originalImage.draw(in: rect) // ここでサイズがバグる
}
image.unlockFocus()
順次大丈夫なパターンとダメなパターンを見つけたら追加していく。
Discussion
よく見たら、ダメなパターンの
ctx
全く使ってないじゃん。