-
Notifications
You must be signed in to change notification settings - Fork 173
Allow PartsRepresentable
to throw errors
#88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
2f50a29
almost nonbreaking change
morganchen12 1585a96
style
morganchen12 b47d4dd
make partsvalue accessible only when error is never
morganchen12 ed2b972
fix tests
morganchen12 5088f30
fix macos
morganchen12 9913ae5
put errors into api methods
morganchen12 4b0a290
style
morganchen12 a68afb4
remove generic error
morganchen12 0a8fa14
add non-erroring protocol so force unwraps arent required
morganchen12 5412c57
api review feedback: use more specific error case and add failure tests
morganchen12 b744775
specialize error
morganchen12 3282cfd
Merge branch 'main' into mc/error
morganchen12 de42928
style
morganchen12 0adb052
code feedback changes
morganchen12 d962763
use partsValue
morganchen12 183f14e
use consistent closure name
morganchen12 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import UniformTypeIdentifiers | ||
#if canImport(UIKit) | ||
import UIKit // For UIImage extensions. | ||
#elseif canImport(AppKit) | ||
import AppKit // For NSImage extensions. | ||
#endif | ||
|
||
private let imageCompressionQuality: CGFloat = 0.8 | ||
|
||
/// An enum describing failures that can occur when converting image types to model content data. | ||
/// For some image types like `CIImage`, creating valid model content requires creating a JPEG | ||
/// representation of the image that may not yet exist, which may be computationally expensive. | ||
public enum ImageConversionError: Error { | ||
/// The image (the receiver of the call `toModelContentParts()`) was invalid. | ||
case invalidUnderlyingImage | ||
|
||
/// A valid image destination could not be allocated. | ||
case couldNotAllocateDestination | ||
|
||
/// JPEG image data conversion failed, accompanied by the original image, which may be an | ||
/// instance of `NSImageRep`, `UIImage`, `CGImage`, or `CIImage`. | ||
case couldNotConvertToJPEG(Any) | ||
} | ||
|
||
#if canImport(UIKit) | ||
/// Enables images to be representable as ``ThrowingPartsRepresentable``. | ||
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *) | ||
extension UIImage: ThrowingPartsRepresentable { | ||
public func tryPartsValue() throws -> [ModelContent.Part] { | ||
guard let data = jpegData(compressionQuality: imageCompressionQuality) else { | ||
throw ImageConversionError.couldNotConvertToJPEG(self) | ||
} | ||
return [ModelContent.Part.data(mimetype: "image/jpeg", data)] | ||
} | ||
} | ||
|
||
#elseif canImport(AppKit) | ||
/// Enables images to be representable as ``ThrowingPartsRepresentable``. | ||
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *) | ||
extension NSImage: ThrowingPartsRepresentable { | ||
public func tryPartsValue() throws -> [ModelContent.Part] { | ||
guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else { | ||
throw ImageConversionError.invalidUnderlyingImage | ||
} | ||
let bmp = NSBitmapImageRep(cgImage: cgImage) | ||
guard let data = bmp.representation(using: .jpeg, properties: [.compressionFactor: 0.8]) | ||
else { | ||
throw ImageConversionError.couldNotConvertToJPEG(bmp) | ||
} | ||
return [ModelContent.Part.data(mimetype: "image/jpeg", data)] | ||
} | ||
} | ||
#endif | ||
|
||
/// Enables `CGImages` to be representable as model content. | ||
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *) | ||
extension CGImage: ThrowingPartsRepresentable { | ||
public func tryPartsValue() throws -> [ModelContent.Part] { | ||
let output = NSMutableData() | ||
guard let imageDestination = CGImageDestinationCreateWithData( | ||
output, UTType.jpeg.identifier as CFString, 1, nil | ||
) else { | ||
throw ImageConversionError.couldNotAllocateDestination | ||
} | ||
CGImageDestinationAddImage(imageDestination, self, nil) | ||
CGImageDestinationSetProperties(imageDestination, [ | ||
kCGImageDestinationLossyCompressionQuality: imageCompressionQuality, | ||
] as CFDictionary) | ||
if CGImageDestinationFinalize(imageDestination) { | ||
return [.data(mimetype: "image/jpeg", output as Data)] | ||
} | ||
throw ImageConversionError.couldNotConvertToJPEG(self) | ||
} | ||
} | ||
|
||
/// Enables `CIImages` to be representable as model content. | ||
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *) | ||
extension CIImage: ThrowingPartsRepresentable { | ||
public func tryPartsValue() throws -> [ModelContent.Part] { | ||
let context = CIContext() | ||
let jpegData = (colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)) | ||
.flatMap { | ||
// The docs specify kCGImageDestinationLossyCompressionQuality as a supported option, but | ||
// Swift's type system does not allow this. | ||
// [kCGImageDestinationLossyCompressionQuality: imageCompressionQuality] | ||
context.jpegRepresentation(of: self, colorSpace: $0, options: [:]) | ||
} | ||
if let jpegData = jpegData { | ||
return [.data(mimetype: "image/jpeg", jpegData)] | ||
} | ||
throw ImageConversionError.couldNotConvertToJPEG(self) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.