Rework image compression / upload
This commit is contained in:
parent
0d2454886e
commit
f172d6d4a6
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "emojitext",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/divadretlaw/EmojiText",
|
||||
"state" : {
|
||||
"revision" : "b5b0a30933a6dcb6601ad3625690a823fa3f6965",
|
||||
"version" : "2.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "keychain-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/evgenyneu/keychain-swift",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "c1fde55798b164cad44b5e23cfa2f0f1ebcd76af"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nuke",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke",
|
||||
"state" : {
|
||||
"revision" : "6241e100294a2aa70d1811641585ab7da780bd0f",
|
||||
"version" : "12.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftsoup",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
||||
"state" : {
|
||||
"revision" : "f707b8680cddb96dc1855632340a572ef37bbb98",
|
||||
"version" : "2.5.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftui-shimmer",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/markiv/SwiftUI-Shimmer",
|
||||
"state" : {
|
||||
"revision" : "965a7cbcbf094cbcf22b9251a2323bdc3432e171",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
|
@ -7,7 +7,49 @@ actor StatusEditorCompressor {
|
|||
case noData
|
||||
}
|
||||
|
||||
func compressImage(_ image: UIImage) async throws -> Data {
|
||||
func compressImageFrom(url: URL) async -> Data? {
|
||||
return await withCheckedContinuation{ continuation in
|
||||
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
||||
guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let downsampleOptions = [
|
||||
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||
kCGImageSourceCreateThumbnailWithTransform: true,
|
||||
kCGImageSourceThumbnailMaxPixelSize: 4096,
|
||||
] as CFDictionary
|
||||
|
||||
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else {
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let data = NSMutableData()
|
||||
guard let imageDestination = CGImageDestinationCreateWithData(data, UTType.jpeg.identifier as CFString, 1, nil) else {
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let isPNG: Bool = {
|
||||
guard let utType = cgImage.utType else { return false }
|
||||
return (utType as String) == UTType.png.identifier
|
||||
|
||||
}()
|
||||
|
||||
let destinationProperties = [
|
||||
kCGImageDestinationLossyCompressionQuality: isPNG ? 1.0 : 0.75
|
||||
] as CFDictionary
|
||||
|
||||
CGImageDestinationAddImage(imageDestination, cgImage, destinationProperties)
|
||||
CGImageDestinationFinalize(imageDestination)
|
||||
|
||||
continuation.resume(returning: data as Data)
|
||||
}
|
||||
}
|
||||
|
||||
func compressImageForUpload(_ image: UIImage) async throws -> Data {
|
||||
var image = image
|
||||
if image.size.height > 5000 || image.size.width > 5000 {
|
||||
image = image.resized(to: .init(width: image.size.width / 4,
|
||||
|
|
|
@ -61,12 +61,14 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
|||
func loadItemContent(item: NSItemProvider) async throws -> Any? {
|
||||
// Many warnings here about non-sendable type `[AnyHashable: Any]?` crossing
|
||||
// actor boundaries. Many Radars have been filed.
|
||||
let result = try await item.loadItem(forTypeIdentifier: rawValue)
|
||||
if isVideo, let transferable = await getVideoTransferable(item: item) {
|
||||
return transferable
|
||||
} else if isGif, let transferable = await getGifTransferable(item: item) {
|
||||
return transferable
|
||||
} else if let transferable = await getImageTansferable(item: item) {
|
||||
return transferable
|
||||
}
|
||||
let result = try await item.loadItem(forTypeIdentifier: rawValue)
|
||||
if self == .jpeg || self == .png || self == .tiff || self == .image || self == .uiimage || self == .adobeRawImage {
|
||||
if let image = result as? UIImage {
|
||||
return image
|
||||
|
@ -79,8 +81,6 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
|||
let image = UIImage(data: data)
|
||||
{
|
||||
return image
|
||||
} else if let transferable = await getImageTansferable(item: item) {
|
||||
return transferable
|
||||
}
|
||||
}
|
||||
if let url = result as? URL {
|
||||
|
@ -149,9 +149,6 @@ struct MovieFileTranseferable: Transferable {
|
|||
struct ImageFileTranseferable: Transferable {
|
||||
let url: URL
|
||||
|
||||
lazy var data: Data? = try? Data(contentsOf: url)
|
||||
lazy var image: UIImage? = UIImage(data: data ?? Data())
|
||||
|
||||
static var transferRepresentation: some TransferRepresentation {
|
||||
FileRepresentation(contentType: .image) { image in
|
||||
SentTransferredFile(image.url)
|
||||
|
|
|
@ -379,6 +379,7 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
let handledItemType = StatusEditorUTTypeSupported(rawValue: identifier)
|
||||
{
|
||||
do {
|
||||
let compressor = StatusEditorCompressor()
|
||||
let content = try await handledItemType.loadItemContent(item: item)
|
||||
if let text = content as? String {
|
||||
initialText += "\(text) "
|
||||
|
@ -388,9 +389,9 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
gifTransferable: nil,
|
||||
mediaAttachment: nil,
|
||||
error: nil))
|
||||
} else if var content = content as? ImageFileTranseferable,
|
||||
let image = content.image
|
||||
{
|
||||
} else if let content = content as? ImageFileTranseferable,
|
||||
let compressedData = await compressor.compressImageFrom(url: content.url),
|
||||
let image = UIImage(data: compressedData) {
|
||||
mediasImages.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
|
@ -532,7 +533,6 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
Task {
|
||||
var medias: [StatusEditorMediaContainer] = []
|
||||
for media in selectedMedias {
|
||||
print(media.supportedContentTypes)
|
||||
var file: (any Transferable)?
|
||||
|
||||
if file == nil {
|
||||
|
@ -545,8 +545,10 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
file = try? await media.loadTransferable(type: ImageFileTranseferable.self)
|
||||
}
|
||||
|
||||
if var imageFile = file as? ImageFileTranseferable,
|
||||
let image = imageFile.image
|
||||
let compressor = StatusEditorCompressor()
|
||||
if let imageFile = file as? ImageFileTranseferable,
|
||||
let compressedData = await compressor.compressImageFrom(url: imageFile.url),
|
||||
let image = UIImage(data: compressedData)
|
||||
{
|
||||
medias.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
|
@ -602,7 +604,7 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
if let index = indexOf(container: newContainer) {
|
||||
let compressor = StatusEditorCompressor()
|
||||
if let image = originalContainer.image {
|
||||
let imageData = try await compressor.compressImage(image)
|
||||
let imageData = try await compressor.compressImageForUpload(image)
|
||||
let uploadedMedia = try await uploadMedia(data: imageData, mimeType: "image/jpeg")
|
||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: nil,
|
||||
|
|
Loading…
Reference in New Issue