実装
- 検索するといくつも実装例が見つかりますが、最終的に下記を参考にしました。
- NSImage をリサイズする。
- Swiftのバージョンが古かったため、いくつかリファクタリングしています。
extension NSImage {
func pixelsSize() -> NSSize? {
guard let pixelsWide = self.tiffRepresentation?.bitmap?.pixelsWide,
let pixelsHigh = self.tiffRepresentation?.bitmap?.pixelsHigh else {
return nil
}
return NSSize(width: pixelsWide, height: pixelsHigh)
}
func resizedImageScaseDown(to ratio: Double) -> NSImage? {
if !(0.0 < ratio && ratio < 1.0) {
return nil
}
if !self.isValid {
return nil
}
guard let sourceBitmapRep = self.tiffRepresentation?.bitmap else {
return nil
}
// 画像のサイズと実際の描画サイズ(ピクセル単位)の2つを使用する
let newImageSize = NSSize(width: Double(self.size.width) * ratio,
height: Double(self.size.height) * ratio)
guard let pixelsSize = pixelsSize() else {
return nil
}
let newPixelsSize = NSSize(width: Double(pixelsSize.width) * ratio,
height: Double(pixelsSize.height) * ratio)
let bitmapRep = NSBitmapImageRep(bitmapDataPlanes: nil,
pixelsWide: Int(newPixelsSize.width),
pixelsHigh: Int(newPixelsSize.height),
bitsPerSample: sourceBitmapRep.bitsPerSample,
samplesPerPixel: sourceBitmapRep.samplesPerPixel,
hasAlpha: sourceBitmapRep.hasAlpha,
isPlanar: sourceBitmapRep.isPlanar,
colorSpaceName: sourceBitmapRep.colorSpaceName,
bytesPerRow: sourceBitmapRep.bytesPerRow,
bitsPerPixel: sourceBitmapRep.bitsPerPixel)!
bitmapRep.size = newImageSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext.init(bitmapImageRep: bitmapRep)
self.draw(in: NSRect(x: 0, y: 0, width: newImageSize.width, height: newImageSize.height),
from: NSRect.zero,
operation: NSCompositingOperation.copy,
fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()
let newImage = NSImage(size: newImageSize)
newImage.addRepresentation(bitmapRep)
return newImage
}
}
- 下記3つの画像のサイズに関するパラメータがあります。
- 解像度に影響するので、比率を合わせるように実装しています。
パラメータ | 例 |
---|---|
NSImage.size | (1680, 1050) |
NSBitmapImageRep.size | (1680, 1050) |
NSBitmapImageRep.pixels | (3360, 2100) |
- また実装を便利にするため、下記のextensionを定義しています。
extension NSBitmapImageRep {
func imageWithFormat(for format: NSBitmapImageRep.FileType) -> Data? {
return representation(using: format, properties: [:])
}
}
extension Data {
var bitmap: NSBitmapImageRep? { NSBitmapImageRep(data: self) }
}
extension NSImage {
var png : Data? { tiffRepresentation?.bitmap?.imageWithFormat(for: .png) }
var jpeg: Data? { tiffRepresentation?.bitmap?.imageWithFormat(for: .jpeg) }
var gif : Data? { tiffRepresentation?.bitmap?.imageWithFormat(for: .gif) }
}
- 呼び出し例は下記のとおりです。
guard let image = loadImage() else {
return
}
// 50%のサイズにする
guard let resizedImage = image.resizedImageScaseDown(to: 0.5) else {
return
}
let outputURL1 = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!.appendingPathComponent("/images/created_image_1.png")
let outputURL2 = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!.appendingPathComponent("/images/created_image_2.png")
if let imageData = image.png,
let resizedImageData = resizedImage.png {
do {
print(imageData.count)
print(resizedImageData.count)
try imageData.write(to: outputURL1)
try resizedImageData.write(to: outputURL2)
print("PNG image saved")
} catch {
print(error)
}
}
// MARK: - Helper Methods
func loadImage() -> NSImage? {
let desktopPath = (NSSearchPathForDirectoriesInDomains(.desktopDirectory, .userDomainMask, true) as [String]).first
let filePath = desktopPath?.appending("/images/desktop 1.png")
if let filePath = filePath {
if let image = NSImage(contentsOfFile: filePath) {
return image
}
}
return nil
}
- 出力結果は以下の通りです。
- 縦横のサイズがそれぞれ50%になっていることが確認できます。
解決していない問題
- ご存じの方、お教えいただけると助かります…。
NSImageを経由するとサイズが異なる
- 読み込み元のファイルをそのまま出力しているつもりですが、ファイルサイズに微妙な差異が見られます。
- representation(using:properties:)のpropertiesで何かしら設定しないといけない?

ColorSyncプロファイルが異なる
