LoginSignup
1
2

More than 3 years have passed since last update.

NSImageのサイズを縮小してPNGファイルを出力する

Last updated at Posted at 2020-08-07

実装

  • 検索するといくつも実装例が見つかりますが、最終的に下記を参考にしました。
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
    }
}
パラメータ
NSImage.size (1680, 1050)
NSBitmapImageRep.size (1680, 1050)
NSBitmapImageRep.pixels (3360, 2100)
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%になっていることが確認できます。

-w628

解決していない問題

  • ご存じの方、お教えいただけると助かります…。

NSImageを経由するとサイズが異なる

  • 読み込み元のファイルをそのまま出力しているつもりですが、ファイルサイズに微妙な差異が見られます。
  • representation(using:properties:)のpropertiesで何かしら設定しないといけない?

image

ColorSyncプロファイルが異なる

image

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2