forked from huggingface/swift-coreml-diffusers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTextToImage.swift
141 lines (127 loc) · 5.24 KB
/
TextToImage.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//
// TextToImage.swift
// Diffusion
//
// Created by Pedro Cuenca on December 2022.
// See LICENSE at https://github.com/huggingface/swift-coreml-diffusers/LICENSE
//
import SwiftUI
import Combine
import StableDiffusion
/// Presents "Share" + "Save" buttons on Mac; just "Share" on iOS/iPadOS.
/// This is because I didn't find a way for "Share" to show a Save option when running on macOS.
struct ShareButtons: View {
var image: CGImage
var name: String
var filename: String {
name.replacingOccurrences(of: " ", with: "_")
}
var body: some View {
let imageView = Image(image, scale: 1, label: Text(name))
if runningOnMac {
HStack {
ShareLink(item: imageView, preview: SharePreview(name, image: imageView))
Button() {
guard let imageData = UIImage(cgImage: image).pngData() else {
return
}
do {
let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(filename).png")
try imageData.write(to: fileURL)
let controller = UIDocumentPickerViewController(forExporting: [fileURL])
let scene = UIApplication.shared.connectedScenes.first as! UIWindowScene
scene.windows.first!.rootViewController!.present(controller, animated: true)
} catch {
print("Error creating file")
}
} label: {
Label("Save…", systemImage: "square.and.arrow.down")
}
}
} else {
ShareLink(item: imageView, preview: SharePreview(name, image: imageView))
}
}
}
struct ImageWithPlaceholder: View {
@EnvironmentObject var generation: GenerationContext
var state: Binding<GenerationState>
var body: some View {
switch state.wrappedValue {
case .startup: return AnyView(Image("placeholder").resizable())
case .running(let progress):
guard let progress = progress, progress.stepCount > 0 else {
// The first time it takes a little bit before generation starts
return AnyView(ProgressView())
}
let step = Int(progress.step) + 1
let fraction = Double(step) / Double(progress.stepCount)
let label = "Step \(step) of \(progress.stepCount)"
return AnyView(VStack {
Group {
if let safeImage = generation.previewImage {
Image(safeImage, scale: 1, label: Text("generated"))
.resizable()
.clipShape(RoundedRectangle(cornerRadius: 20))
}
}
ProgressView(label, value: fraction, total: 1).padding()
})
case .complete(let lastPrompt, let image, _, let interval):
guard let theImage = image else {
return AnyView(Image(systemName: "exclamationmark.triangle").resizable())
}
let imageView = Image(theImage, scale: 1, label: Text("generated"))
return AnyView(
VStack {
imageView.resizable().clipShape(RoundedRectangle(cornerRadius: 20))
HStack {
let intervalString = String(format: "Time: %.1fs", interval ?? 0)
Rectangle().fill(.clear).overlay(Text(intervalString).frame(maxWidth: .infinity, alignment: .leading).padding(.leading))
Rectangle().fill(.clear).overlay(
HStack {
Spacer()
ShareButtons(image: theImage, name: lastPrompt).padding(.trailing)
}
)
}.frame(maxHeight: 25)
})
case .failed(_):
return AnyView(Image(systemName: "exclamationmark.triangle").resizable())
case .userCanceled:
return AnyView(Text("Generation canceled"))
}
}
}
struct TextToImage: View {
@EnvironmentObject var generation: GenerationContext
func submit() {
if case .running = generation.state { return }
Task {
generation.state = .running(nil)
do {
let result = try await generation.generate()
generation.state = .complete(generation.positivePrompt, result.image, result.lastSeed, result.interval)
} catch {
generation.state = .failed(error)
}
}
}
var body: some View {
VStack {
HStack {
PromptTextField(text: $generation.positivePrompt, isPositivePrompt: true, model: iosModel().modelVersion)
Button("Generate") {
submit()
}
.padding()
.buttonStyle(.borderedProminent)
}
ImageWithPlaceholder(state: $generation.state)
.scaledToFit()
Spacer()
}
.padding()
.environmentObject(generation)
}
}