Diffusion/Common/DiffusionImage.swift (95 lines of code) (raw):

// // DiffusionImage.swift // Diffusion // // Created by Dolmere on 03/07/2023. // import SwiftUI import StableDiffusion import CoreTransferable /// Tracking for a `DiffusionImage` generation state. enum DiffusionImageState { case generating case waiting case complete } /// Generic custom error to use when an image generation fails. enum DiffusionImageError: Error { case invalidDiffusionImage } /// Combination of a `DiffusionImage` and its associated `DiffusionImageState` struct DiffusionImageWrapper { var diffusionImageState: DiffusionImageState = .waiting var diffusionImage: DiffusionImage? = nil } /// Model class to hold a generated image and the "recipe" data that was used to generate it final class DiffusionImage: NSObject, Identifiable, NSCoding, NSSecureCoding { let id: UUID let cgImage: CGImage let seed: UInt32 let steps: Double let positivePrompt: String let negativePrompt: String let guidanceScale: Double let disableSafety: Bool /// Local enum represented with a String to conform to NSSecureCoding let scheduler: StableDiffusionScheduler /// This is a composed `String` built from the numeric `Seed` and the user supplied `positivePrompt` limited to the first 200 character and with whitespace replaced with underscore characters. var generatedFilename: String { return "\(seed)-\(positivePrompt)".first200Safe } /// The location on the file system where this generated image is stored. var fileURL: URL init(id: UUID, cgImage: CGImage, seed: UInt32, steps: Double, positivePrompt: String, negativePrompt: String, guidanceScale: Double, disableSafety: Bool, scheduler: StableDiffusionScheduler) { let genname = "\(seed)-\(positivePrompt)".first200Safe self.id = id self.cgImage = cgImage self.seed = seed self.steps = steps self.positivePrompt = positivePrompt self.negativePrompt = negativePrompt self.guidanceScale = guidanceScale self.disableSafety = disableSafety self.scheduler = scheduler // Initially set the fileURL to the top level applicationDirectory to allow running the completed instance func save() where the fileURL will be updated to the correct location. self.fileURL = URL.applicationDirectory // init the instance fully before executing an instance function super.init() if let url = save(cgImage: cgImage, filename: genname) { self.fileURL = url } else { fatalError("Fatal error init of DiffusionImage, cannot create image file at \(genname)") } } func encode(with coder: NSCoder) { coder.encode(id, forKey: "id") coder.encode(seed, forKey: "seed") coder.encode(steps, forKey: "steps") coder.encode(positivePrompt, forKey: "positivePrompt") coder.encode(negativePrompt, forKey: "negativePrompt") coder.encode(guidanceScale, forKey: "guidanceScale") coder.encode(disableSafety, forKey: "disableSafety") coder.encode(scheduler, forKey: "scheduler") // Encode cgImage as data if let data = pngRepresentation() { coder.encode(data, forKey: "cgImage") } } required init?(coder: NSCoder) { guard let id = coder.decodeObject(forKey: "id") as? UUID else { return nil } self.id = id self.seed = UInt32(coder.decodeInt32(forKey: "seed")) self.steps = coder.decodeDouble(forKey: "steps") self.positivePrompt = coder.decodeObject(forKey: "positivePrompt") as? String ?? "" self.negativePrompt = coder.decodeObject(forKey: "negativePrompt") as? String ?? "" self.guidanceScale = coder.decodeDouble(forKey: "guidanceScale") self.disableSafety = coder.decodeBool(forKey: "disableSafety") self.scheduler = coder.decodeObject(forKey: "scheduler") as? StableDiffusionScheduler ?? StableDiffusionScheduler.dpmSolverMultistepScheduler let genname = "\(seed)-\(positivePrompt)".first200Safe // Decode cgImage from data if let imageData = coder.decodeObject(forKey: "cgImage") as? Data { guard let img = CGImage.fromData(imageData) else { fatalError("Fatal error loading data with missing cgImage in object") } self.cgImage = img } else { fatalError("Fatal error loading data with missing cgImage in object") } self.fileURL = URL.applicationDirectory super.init() if let url = save(cgImage: cgImage, filename: genname) { self.fileURL = url } else { fatalError("Fatal error init of DiffusionImage, cannot create image file at \(genname)") } } // MARK: - Equatable static func == (lhs: DiffusionImage, rhs: DiffusionImage) -> Bool { return lhs.id == rhs.id } // MARK: - NSSecureCoding static var supportsSecureCoding: Bool { return true } }