Skip to content

Instantly share code, notes, and snippets.

@mjstephan
Forked from pd95/ContentView.swift
Created August 5, 2021 21:02
Show Gist options
  • Save mjstephan/419a72334b871c05262374e43c17b6c3 to your computer and use it in GitHub Desktop.
Save mjstephan/419a72334b871c05262374e43c17b6c3 to your computer and use it in GitHub Desktop.

Revisions

  1. @pd95 pd95 revised this gist May 31, 2020. 1 changed file with 68 additions and 79 deletions.
    147 changes: 68 additions & 79 deletions ContentView.swift
    Original file line number Diff line number Diff line change
    @@ -1,38 +1,37 @@
    //
    // ContentView.swift
    // Instafilter
    // Project13
    //
    // Created by Philipp on 18.04.20.
    // Copyright © 2020 Philipp. All rights reserved.
    // Created by Paul Hudson on 17/02/2020.
    // Copyright © 2020 Paul Hudson. All rights reserved.
    //

    import SwiftUI
    import CoreImage
    import CoreImage.CIFilterBuiltins
    import SwiftUI

    struct ContentView: View {
    @State private var inputImage: UIImage?
    @State private var processedImage: UIImage?
    @State private var image: Image?
    @State private var filterIntensity = 0.5
    @State private var filterRadius = 100.0
    @State private var filterScale = 20.0
    @State private var showingImagePicker = false

    @State private var showingFilterSheet = false

    @State private var showingImagePicker = false
    @State private var processedImage: UIImage?
    @State private var inputImage: UIImage?

    @State private var currentFilter: CIFilter = CIFilter.sepiaTone()
    let context = CIContext()

    @State private var showAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""

    @State private var currentFilter : CIFilter = CIFilter.sepiaTone()
    let context = CIContext()

    let operationQueue = OperationQueue()

    init() {
    operationQueue.maxConcurrentOperationCount = 1
    }

    var body: some View {
    let intensity = Binding<Double>(
    get: {
    @@ -44,32 +43,12 @@ struct ContentView: View {
    }
    )

    let radius = Binding<Double>(
    get: {
    self.filterRadius
    },
    set: {
    self.filterRadius = $0
    self.applyProcessing()
    }
    )

    let scale = Binding<Double>(
    get: {
    self.filterScale
    },
    set: {
    self.filterScale = $0
    self.applyProcessing()
    }
    )

    return NavigationView {
    VStack {
    ZStack {
    Rectangle()
    .fill(Color.secondary)

    if image != nil {
    image?
    .resizable()
    @@ -83,35 +62,19 @@ struct ContentView: View {
    .onTapGesture {
    self.showingImagePicker = true
    }

    if self.hasIntensity {
    HStack {
    Text("Intensity")
    Slider(value: intensity)
    }.padding(.vertical)
    }

    if self.hasRadius {
    HStack {
    Text("Radius")
    Slider(value: radius, in: 0...200)
    }.padding(.vertical)
    }

    if self.hasScale {
    HStack {
    Text("Scale")
    Slider(value: scale, in: 0...100)
    }.padding(.vertical)
    }


    HStack {
    Text("Intensity")
    Slider(value: intensity)
    }.padding(.vertical)

    HStack {
    Button("Change Filter") {
    self.showingFilterSheet = true
    }

    Spacer()

    Button("Save") {
    guard let processedImage = self.processedImage else {
    self.showAlert = true
    @@ -172,16 +135,32 @@ struct ContentView: View {
    applyProcessing()
    }

    func setFilter(_ filter: CIFilter) {
    currentFilter = filter
    loadImage()
    var hasIntensity: Bool {
    currentFilter.inputKeys.contains(kCIInputIntensityKey)
    }

    var hasRadius: Bool {
    currentFilter.inputKeys.contains(kCIInputRadiusKey)
    }

    var hasScale: Bool {
    currentFilter.inputKeys.contains(kCIInputScaleKey)
    }

    func applyProcessing() {
    if hasIntensity { currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) }
    if hasRadius { currentFilter.setValue(filterRadius, forKey: kCIInputRadiusKey) }
    if hasScale { currentFilter.setValue(filterScale, forKey: kCIInputScaleKey) }

    if hasIntensity {
    print("set intensity \(filterIntensity)")
    currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey)
    }
    if hasRadius {
    print("set radius \(filterIntensity * 200)")
    currentFilter.setValue(filterIntensity * 200, forKey: kCIInputRadiusKey)
    }
    if hasScale {
    print("set scale \(filterIntensity * 10)")
    currentFilter.setValue(filterIntensity * 10, forKey: kCIInputScaleKey)
    }

    guard let outputImage = currentFilter.outputImage else { return }

    operationQueue.cancelAllOperations()
    @@ -197,23 +176,33 @@ struct ContentView: View {
    }
    operationQueue.addOperation(operation)
    }

    var hasIntensity: Bool {
    currentFilter.inputKeys.contains(kCIInputIntensityKey)
    }

    var hasRadius: Bool {
    currentFilter.inputKeys.contains(kCIInputRadiusKey)
    }

    var hasScale: Bool {
    currentFilter.inputKeys.contains(kCIInputScaleKey)

    func setFilter(_ filter: CIFilter) {
    currentFilter = filter
    loadImage()
    }

    }

    struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
    ContentView()
    }
    }


    extension CGImagePropertyOrientation {
    init(_ uiOrientation: UIImage.Orientation) {
    switch uiOrientation {
    case .up: self = .up
    case .upMirrored: self = .upMirrored
    case .down: self = .down
    case .downMirrored: self = .downMirrored
    case .left: self = .left
    case .leftMirrored: self = .leftMirrored
    case .right: self = .right
    case .rightMirrored: self = .rightMirrored
    @unknown default:
    fatalError("Unknown uiOrientation \(uiOrientation)")
    }
    }
    }
  2. @pd95 pd95 created this gist May 23, 2020.
    219 changes: 219 additions & 0 deletions ContentView.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,219 @@
    //
    // ContentView.swift
    // Instafilter
    //
    // Created by Philipp on 18.04.20.
    // Copyright © 2020 Philipp. All rights reserved.
    //

    import SwiftUI
    import CoreImage
    import CoreImage.CIFilterBuiltins

    struct ContentView: View {
    @State private var inputImage: UIImage?
    @State private var processedImage: UIImage?
    @State private var image: Image?
    @State private var filterIntensity = 0.5
    @State private var filterRadius = 100.0
    @State private var filterScale = 20.0
    @State private var showingImagePicker = false
    @State private var showingFilterSheet = false

    @State private var showAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""

    @State private var currentFilter : CIFilter = CIFilter.sepiaTone()
    let context = CIContext()

    let operationQueue = OperationQueue()

    init() {
    operationQueue.maxConcurrentOperationCount = 1
    }

    var body: some View {
    let intensity = Binding<Double>(
    get: {
    self.filterIntensity
    },
    set: {
    self.filterIntensity = $0
    self.applyProcessing()
    }
    )

    let radius = Binding<Double>(
    get: {
    self.filterRadius
    },
    set: {
    self.filterRadius = $0
    self.applyProcessing()
    }
    )

    let scale = Binding<Double>(
    get: {
    self.filterScale
    },
    set: {
    self.filterScale = $0
    self.applyProcessing()
    }
    )

    return NavigationView {
    VStack {
    ZStack {
    Rectangle()
    .fill(Color.secondary)

    if image != nil {
    image?
    .resizable()
    .scaledToFit()
    } else {
    Text("Tap to select a picture")
    .foregroundColor(.white)
    .font(.headline)
    }
    }
    .onTapGesture {
    self.showingImagePicker = true
    }

    if self.hasIntensity {
    HStack {
    Text("Intensity")
    Slider(value: intensity)
    }.padding(.vertical)
    }

    if self.hasRadius {
    HStack {
    Text("Radius")
    Slider(value: radius, in: 0...200)
    }.padding(.vertical)
    }

    if self.hasScale {
    HStack {
    Text("Scale")
    Slider(value: scale, in: 0...100)
    }.padding(.vertical)
    }

    HStack {
    Button("Change Filter") {
    self.showingFilterSheet = true
    }

    Spacer()

    Button("Save") {
    guard let processedImage = self.processedImage else {
    self.showAlert = true
    self.alertTitle = "Cannot save"
    self.alertMessage = "No image has been processed yet."
    return
    }

    let imageSaver = ImageSaver()

    imageSaver.successHandler = {
    print("Success!")
    }

    imageSaver.errorHandler = {
    print("Oops: \($0.localizedDescription)")
    }

    imageSaver.writeToPhotoAlbum(image: processedImage)
    }
    }
    }
    .padding([.horizontal, .bottom])
    .navigationBarTitle("Instafilter")
    .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
    ImagePicker(image: self.$inputImage)
    }
    .alert(isPresented: $showAlert) {
    Alert(title: Text(self.alertTitle), message: Text(self.alertMessage), dismissButton: .default(Text("OK")))
    }
    .actionSheet(isPresented: self.$showingFilterSheet) {
    ActionSheet(title: Text("Select a filter (current '\(CIFilter.localizedName(forFilterName: self.currentFilter.name) ?? "Unknown Filter"))'"), buttons: [
    .default(Text("Crystallize")) { self.setFilter(CIFilter.crystallize()) },
    .default(Text("Edges")) { self.setFilter(CIFilter.edges()) },
    .default(Text("Gaussian Blur")) { self.setFilter(CIFilter.gaussianBlur()) },
    .default(Text("Pixellate")) { self.setFilter(CIFilter.pixellate()) },
    .default(Text("Sepia Tone")) { self.setFilter(CIFilter.sepiaTone()) },
    .default(Text("Unsharp Mask")) { self.setFilter(CIFilter.unsharpMask()) },
    .default(Text("Vignette")) { self.setFilter(CIFilter.vignette()) },
    .cancel()
    ])
    }
    }
    }

    func loadImage() {
    guard let inputImage = inputImage else { return }

    let beginImage : CIImage
    if let ciImage = inputImage.ciImage {
    beginImage = ciImage
    }
    else {
    beginImage = CIImage(cgImage: inputImage.cgImage!).oriented(CGImagePropertyOrientation(inputImage.imageOrientation))
    }

    currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
    applyProcessing()
    }

    func setFilter(_ filter: CIFilter) {
    currentFilter = filter
    loadImage()
    }

    func applyProcessing() {
    if hasIntensity { currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) }
    if hasRadius { currentFilter.setValue(filterRadius, forKey: kCIInputRadiusKey) }
    if hasScale { currentFilter.setValue(filterScale, forKey: kCIInputScaleKey) }

    guard let outputImage = currentFilter.outputImage else { return }

    operationQueue.cancelAllOperations()
    let operation = BlockOperation()
    operation.addExecutionBlock {
    if let cgimg = self.context.createCGImage(outputImage, from: outputImage.extent) {
    let uiImage = UIImage(cgImage: cgimg)
    DispatchQueue.main.async {
    self.image = Image(uiImage: uiImage)
    self.processedImage = uiImage
    }
    }
    }
    operationQueue.addOperation(operation)
    }

    var hasIntensity: Bool {
    currentFilter.inputKeys.contains(kCIInputIntensityKey)
    }

    var hasRadius: Bool {
    currentFilter.inputKeys.contains(kCIInputRadiusKey)
    }

    var hasScale: Bool {
    currentFilter.inputKeys.contains(kCIInputScaleKey)
    }

    }

    struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
    ContentView()
    }
    }