diff --git a/Tusker/API/InstanceFeatures.swift b/Tusker/API/InstanceFeatures.swift index 95c10a09..39f5ee4d 100644 --- a/Tusker/API/InstanceFeatures.swift +++ b/Tusker/API/InstanceFeatures.swift @@ -76,6 +76,14 @@ struct InstanceFeatures { } } + var needsWideColorGamutHack: Bool { + if case .mastodon(_, .some(let version)) = instanceType { + return version < Version(4, 0, 0) + } else { + return true + } + } + mutating func update(instance: Instance, nodeInfo: NodeInfo?) { let ver = instance.version.lowercased() if ver.contains("glitch") { diff --git a/Tusker/API/PostService.swift b/Tusker/API/PostService.swift index 5628327c..91a689aa 100644 --- a/Tusker/API/PostService.swift +++ b/Tusker/API/PostService.swift @@ -87,7 +87,7 @@ class PostService: ObservableObject { private func getData(for attachment: CompositionAttachment) async throws -> (Data, UTType) { return try await withCheckedThrowingContinuation { continuation in - attachment.data.getData { result in + attachment.data.getData(features: mastodonController.instanceFeatures) { result in switch result { case let .success(res): continuation.resume(returning: res) diff --git a/Tusker/Models/CompositionAttachmentData.swift b/Tusker/Models/CompositionAttachmentData.swift index 6b6c0222..10fec3d8 100644 --- a/Tusker/Models/CompositionAttachmentData.swift +++ b/Tusker/Models/CompositionAttachmentData.swift @@ -51,7 +51,7 @@ enum CompositionAttachmentData { } } - func getData(completion: @escaping (Result<(Data, UTType), Error>) -> Void) { + func getData(features: InstanceFeatures, skipAllConversion: Bool = false, completion: @escaping (Result<(Data, UTType), Error>) -> Void) { switch self { case let .image(image): // Export as JPEG instead of PNG, otherweise photos straight from the camera are too large @@ -71,21 +71,26 @@ enum CompositionAttachmentData { return } + guard !skipAllConversion else { + completion(.success((data, UTType(dataUTI)!))) + return + } + let utType: UTType let image = CIImage(data: data)! - let needsColorSpaceConversion = image.colorSpace?.name != CGColorSpace.sRGB + let needsColorSpaceConversion = features.needsWideColorGamutHack && image.colorSpace?.name != CGColorSpace.sRGB // neither Mastodon nor Pleroma handles HEIC well, so convert to JPEG // they also do a bad job converting wide color gamut images (they seem to just drop the profile, letting the wide-gamut values be reinterprete as sRGB) // if that changes in the future, we'll need to pass the InstanceFeatures in here somehow and gate the conversion if needsColorSpaceConversion || dataUTI == "public.heic" { let context = CIContext() - let sRGB = CGColorSpace(name: CGColorSpace.sRGB)! + let colorSpace = needsColorSpaceConversion || image.colorSpace != nil ? CGColorSpace(name: CGColorSpace.sRGB)! : image.colorSpace! if dataUTI == "public.png" { - data = context.pngRepresentation(of: image, format: .ARGB8, colorSpace: sRGB)! + data = context.pngRepresentation(of: image, format: .ARGB8, colorSpace: colorSpace)! utType = .png } else { - data = context.jpegRepresentation(of: image, colorSpace: sRGB)! + data = context.jpegRepresentation(of: image, colorSpace: colorSpace)! utType = .jpeg } } else { diff --git a/Tusker/Screens/Compose/ComposeAttachmentRow.swift b/Tusker/Screens/Compose/ComposeAttachmentRow.swift index b08e971c..ab26294c 100644 --- a/Tusker/Screens/Compose/ComposeAttachmentRow.swift +++ b/Tusker/Screens/Compose/ComposeAttachmentRow.swift @@ -15,6 +15,7 @@ struct ComposeAttachmentRow: View { @ObservedObject var draft: Draft @ObservedObject var attachment: CompositionAttachment + @EnvironmentObject var mastodonController: MastodonController @EnvironmentObject var uiState: ComposeUIState @State private var mode: Mode = .allowEntry @State private var isShowingTextRecognitionFailedAlert = false @@ -90,7 +91,7 @@ struct ComposeAttachmentRow: View { mode = .recognizingText DispatchQueue.global(qos: .userInitiated).async { - self.attachment.data.getData { (result) in + self.attachment.data.getData(features: mastodonController.instanceFeatures, skipAllConversion: true) { (result) in let data: Data do { try data = result.get().0