Reputation: 7367
I want to perform an edit, a JEPG compression for example, on an image from the gallery (taken by iPhone camera) but it fails when the input image is an HEIC image but works with JPEG images.
I retrieve the image within a PHAsset
object via UIImagePickerController
method:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset {
self.asset = asset
}
//...
}
This function edits the selected image:
func editImage() {
if let _asset = self.asset {
_asset.requestContentEditingInput(with: nil, completionHandler: { (contentEditingInput, info) in
let fullURL: URL?
fullURL = contentEditingInput!.fullSizeImageURL
let output = PHContentEditingOutput(contentEditingInput:
contentEditingInput!)
let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: "HEICEditor", requiringSecureCoding: false)
let adjustmentData =
PHAdjustmentData(formatIdentifier:
"HEICEditor.App",
formatVersion: "1.0",
data: archivedData!)
output.adjustmentData = adjustmentData
let imageData = UIImage.init(contentsOfFile: fullURL!.path)?.jpegData(compressionQuality: 0.5)
do {
try imageData!.write(to: output.renderedContentURL, options: .atomic)
} catch let error {
print("error writing data:\(error)")
}
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest(for: _asset)
request.contentEditingOutput = output
}, completionHandler: { (result, error) in
print("error writing data:\(error)")
})
})
}
}
The project with a sample HEIC
image is available at https://github.com/maysamsh/Swift-Playground-EditHEIC
Note 1: With EXIF viewers you can find out if the image is HEIC, or after selecting an image and clicking 'Edit Image' button you can see the full name on top of the image preview.
Note 2: For some reason when I send the HEIC image from iPhone to Mac and send it back to the iPhone it works on the new copy, which is still HEIC and preserves the the original image orientation.
Upvotes: 5
Views: 1228
Reputation: 7367
Here you go, this is how it works: create a CGImageDestination
, write the output at .renderedContentURL
, perform the PHAssetChangeRequest()
:
func editImage() {
if let _asset = self.asset {
let options = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData)
-> Bool in
return true
}
_asset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
let fullURL: URL?
fullURL = contentEditingInput!.fullSizeImageURL
let output = PHContentEditingOutput(contentEditingInput:
contentEditingInput!)
let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: "HEICEditor", requiringSecureCoding: false)
let adjustmentData =
PHAdjustmentData(formatIdentifier:
"HEICEditor.App",
formatVersion: "1.0",
data: archivedData!)
let orientation = contentEditingInput?.fullSizeImageOrientation
let outputURL = output.renderedContentURL
let cgImage = {
() -> CGImage in
let image = UIImage.init(contentsOfFile: fullURL!.path)!
let imageData = image.jpegData(compressionQuality: 1)
let ciImage = CIImage(data: imageData!)!.oriented(forExifOrientation: orientation!)
return CIContext(options: nil).createCGImage(ciImage, from: ciImage.extent)!
}()
let cgImageDestination = CGImageDestinationCreateWithURL(outputURL as CFURL, kUTTypeJPEG, 1, nil)!
CGImageDestinationAddImage(cgImageDestination, cgImage, [
kCGImageDestinationLossyCompressionQuality as String:0.7
] as CFDictionary)
CGImageDestinationFinalize(cgImageDestination)
output.adjustmentData = adjustmentData
self.infoLabel.text = "fullSizeImageURL: \(fullURL?.lastPathComponent ?? "N/A")\n" +
"renderedContentURL: \(output.renderedContentURL.lastPathComponent)"
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest(for: _asset)
request.contentEditingOutput = output
}, completionHandler: { (result, error) in
print("result: \(result), error: \(String(describing: error))")
})
})
}
}
Upvotes: 3