import Foundation
import ImageIO
import UniformTypeIdentifiers
import CoreVideo
enum ImageWriter {
static func writePNG(_ image: CGImage, to url: URL) -> Bool {
guard let dest = CGImageDestinationCreateWithURL(url as CFURL, UTType.png.identifier as CFString, 1, nil) else { return false }
CGImageDestinationAddImage(dest, image, nil)
return CGImageDestinationFinalize(dest)
}
/// Write HEIC (fallback to JPEG if the HEIC encoder is unavailable). `assetID`,
/// when set, embeds the Apple maker-note asset identifier (Live Photo pairing).
/// Returns the actual file URL written (extension .heic or .jpg).
static func writeStill(_ image: CGImage, baseURL: URL, assetID: String?) -> URL? {
let heicSupported = (CGImageDestinationCopyTypeIdentifiers() as? [String] ?? []).contains(UTType.heic.identifier)
let type = heicSupported ? UTType.heic : UTType.jpeg
let ext = heicSupported ? "heic" : "jpg"
let url = baseURL.deletingPathExtension().appendingPathExtension(ext)
guard let dest = CGImageDestinationCreateWithURL(url as CFURL, type.identifier as CFString, 1, nil) else { return nil }
var opts: [CFString: Any] = [kCGImageDestinationLossyCompressionQuality: 0.9]
if let id = assetID { opts[kCGImagePropertyMakerAppleDictionary] = ["17": id] }
CGImageDestinationAddImage(dest, image, opts as CFDictionary)
return CGImageDestinationFinalize(dest) ? url : nil
}
/// CVPixelBuffer (BGRA) → CGImage (Live Photo key-frame readback, used in a later task).
static func cgImage(from pb: CVPixelBuffer) -> CGImage? {
CVPixelBufferLockBaseAddress(pb, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(pb, .readOnly) }
let w = CVPixelBufferGetWidth(pb), h = CVPixelBufferGetHeight(pb)
let bpr = CVPixelBufferGetBytesPerRow(pb)
guard let base = CVPixelBufferGetBaseAddress(pb) else { return nil }
let cs = CGColorSpaceCreateDeviceRGB()
let bm = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
guard let ctx = CGContext(data: base, width: w, height: h, bitsPerComponent: 8, bytesPerRow: bpr, space: cs, bitmapInfo: bm.rawValue) else { return nil }
return ctx.makeImage()
}
}