import AppKit enum ImageLoaderError: Error { case loadFailed(_ message: String) } struct NSImageLoader: LoaderProtocol { typealias T = Image func load(url: URL) -> T? { guard let image = try? NSImageLoader.loadImage(url: url) else { return nil } return image } func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) } static func loadImage(url: URL) throws -> Image { try autoreleasepool { // Open as a Core Graphics image guard let nsImg = NSImage(contentsOf: url), let image = nsImg.cgImage(forProposedRect: nil, context: nil, hints: nil) else { throw ImageLoaderError.loadFailed("Failed to open image \"\(url.absoluteString)\"") } // Convert to 8-bit ARGB (SRGB) w/ premultiplied alpha let alphaInfo = image.alphaInfo == .none ? CGImageAlphaInfo.noneSkipLast : CGImageAlphaInfo.premultipliedLast guard let colourspace = CGColorSpace(name: CGColorSpace.sRGB), let context = CGContext(data: nil, width: image.width, height: image.height, bitsPerComponent: 8, bytesPerRow: image.width * 4, space: colourspace, bitmapInfo: alphaInfo.rawValue | CGBitmapInfo.byteOrder32Big.rawValue) else { throw ImageLoaderError.loadFailed("Coudn't create graphics context") } let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(image.height)) context.concatenate(flipVertical) context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height)) guard let data = context.data else { throw ImageLoaderError.loadFailed("what") } return Image( pixels: Data(bytes: data, count: 4 * image.width * image.height), width: image.width, height: image.height) } } }