Skip to content

Commit

Permalink
Close #29; Fix compatibility with macOS with compile flags for UIImag…
Browse files Browse the repository at this point in the history
…e and NSImage properties.
  • Loading branch information
MarcoDotIO committed Nov 23, 2022
1 parent fe434cb commit 51f9c17
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 7 deletions.
Empty file removed .github/FUNDING.yml~
Empty file.
Empty file.
Empty file.
4 changes: 2 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build
run: swift build -v -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios13.0-simulator"
run: swift build -v
- name: Run tests
run: xcodebuild test -scheme OpenAIKit -sdk iphonesimulator -destination "OS=16.0,name=iPhone 14 Pro Max"
run: swift test -v
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ OpenAIKit is a community-maintained API for the OpenAI REST endpoint used to get

| Platform | Minimum Swift Version | Installation | Status |
| --- | --- | --- | --- |
| iOS 13.0+ / tvOS 13.0+ / watchOS 6.0+ | 5.5 | [Swift Package Manager](#swift-package-manager) | Fully Tested |
| macOS 10.15+ | N/A | N/A | Work in Progress |
| iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ | 5.5 | [Swift Package Manager](#swift-package-manager) | Fully Tested |

## Installation

Expand Down
82 changes: 82 additions & 0 deletions Sources/OpenAIKit/Extensions/NSImageExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// NSImageExtension.swift
// OpenAIKit
//
// Copyright (c) 2022 MarcoDotIO
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#if os(macOS)
import Cocoa

extension NSImage {
public func pngData(
size: ImageResolutions,
imageInterpolation: NSImageInterpolation = .high
) -> Data? {
var cgSize = CGSize()

switch size {
case .small:
cgSize.width = 256
cgSize.height = 256
break
case .medium:
cgSize.width = 512
cgSize.height = 512
break
case .large:
cgSize.width = 1024
cgSize.height = 1024
break
}

guard let bitmap = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(cgSize.width),
pixelsHigh: Int(cgSize.height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .deviceRGB,
bitmapFormat: [],
bytesPerRow: 0,
bitsPerPixel: 0
) else {
return nil
}

bitmap.size = cgSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmap)
NSGraphicsContext.current?.imageInterpolation = imageInterpolation
draw(
in: NSRect(origin: .zero, size: cgSize),
from: .zero,
operation: .copy,
fraction: 1.0
)
NSGraphicsContext.restoreGraphicsState()

return bitmap.representation(using: .png, properties: [:])
}
}
#endif
27 changes: 27 additions & 0 deletions Sources/OpenAIKit/OpenAI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

import SwiftUI

#if os(iOS) || os(tvOS)
import UIKit
#endif

/// OpenAI provides the needed core functions of OpenAIKit.
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public final class OpenAI {
Expand All @@ -35,6 +39,7 @@ public final class OpenAI {
self.config = config
}

#if os(iOS) || os(tvOS)
/// Input a `Base64` image binary `String` to receive an `UIImage` object.
/// - Parameter b64Data: The `Base64` data itself in `String` form.
/// - Returns: A `UIImage` object.
Expand All @@ -53,6 +58,28 @@ public final class OpenAI {
throw OpenAIError.invalidData
}
}
#endif

#if os(macOS)
/// Input a `Base64` image binary `String` to receive an `NSImage` object.
/// - Parameter b64Data: The `Base64` data itself in `String` form.
/// - Returns: A `UIImage` object.
public func decodeBase64Image(_ b64Data: String) throws -> NSImage {
do {
guard let data = Data(base64Encoded: b64Data) else {
throw OpenAIError.invalidData
}

guard let image = NSImage(data: data) else {
throw OpenAIError.invalidData
}

return image
} catch {
throw OpenAIError.invalidData
}
}
#endif

/// Return a `URL` with the OpenAI API endpoint as the `URL`
/// - Parameter path: The `String` path.
Expand Down
2 changes: 0 additions & 2 deletions Sources/OpenAIKit/Protocols/OpenAIProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
// THE SOFTWARE.
//

import UIKit

public protocol OpenAIProtocol {
// MARK: Models Functions
/// List and describe the various models available in the API. You can refer to the [Models](https://beta.openai.com/docs/models)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public struct ImageEditParameters {
/// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
/// [Learn more.](https://beta.openai.com/docs/guides/safety-best-practices/end-user-ids)
public var user: String?


#if os(iOS) || os(tvOS) || os(watchOS)
public init(
image: UIImage,
mask: UIImage,
Expand All @@ -86,6 +87,34 @@ public struct ImageEditParameters {
throw OpenAIError.invalidData
}
}
#endif

#if os(macOS)
public init(
image: NSImage,
mask: NSImage,
prompt: String,
@Clamped(range: 1...10) numberOfImages: Int = 1,
resolution: ImageResolutions = .large,
responseFormat: ResponseFormat = .url,
user: String? = nil
) throws {
do {
guard let imageData = image.pngData(size: resolution) else { throw OpenAIError.invalidData }
guard let maskData = mask.pngData(size: resolution) else { throw OpenAIError.invalidData }

self.image = FormData(data: imageData, mimeType: "image/png", fileName: "image.png")
self.mask = FormData(data: maskData, mimeType: "image/png", fileName: "mask.png")
self.prompt = prompt
self.numberOfImages = numberOfImages
self.resolution = resolution
self.responseFormat = responseFormat
self.user = user
} catch {
throw OpenAIError.invalidData
}
}
#endif

/// The body of the URL used for OpenAI API requests.
public var body: [String: Any] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public struct ImageVariationParameters {
/// [Learn more.](https://beta.openai.com/docs/guides/safety-best-practices/end-user-ids)
public var user: String?

#if os(iOS) || os(tvOS) || os(watchOS)
public init(
image: UIImage,
@Clamped(range: 1...10) numberOfImages: Int = 1,
Expand All @@ -70,7 +71,30 @@ public struct ImageVariationParameters {
throw OpenAIError.invalidData
}
}
#endif

#if os(macOS)
public init(
image: NSImage,
@Clamped(range: 1...10) numberOfImages: Int = 1,
resolution: ImageResolutions = .large,
responseFormat: ResponseFormat = .url,
user: String? = nil
) throws {
do {
guard let imageData = image.pngData(size: resolution) else { throw OpenAIError.invalidData }

self.image = FormData(data: imageData, mimeType: "image/png", fileName: "image.png")
self.numberOfImages = numberOfImages
self.resolution = resolution
self.responseFormat = responseFormat
self.user = user
} catch {
throw OpenAIError.invalidData
}
}
#endif

/// The body of the URL used for OpenAI API requests.
public var body: [String: Any] {
var result: [String: Any] = ["image": self.image,
Expand Down

0 comments on commit 51f9c17

Please sign in to comment.