[Android] 1.9.1 - EXIF Fixes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
class ImageManager {
|
||||
static let shared = ImageManager()
|
||||
|
||||
154
ios/OpenClimb/Utils/OrientationAwareImage.swift
Normal file
154
ios/OpenClimb/Utils/OrientationAwareImage.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct OrientationAwareImage: View {
|
||||
let imagePath: String
|
||||
let contentMode: ContentMode
|
||||
|
||||
@State private var uiImage: UIImage?
|
||||
@State private var isLoading = true
|
||||
@State private var hasFailed = false
|
||||
|
||||
init(imagePath: String, contentMode: ContentMode = .fit) {
|
||||
self.imagePath = imagePath
|
||||
self.contentMode = contentMode
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let uiImage = uiImage {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: contentMode)
|
||||
} else if hasFailed {
|
||||
Image(systemName: "photo")
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
loadImageWithCorrectOrientation()
|
||||
}
|
||||
.onChange(of: imagePath) { _ in
|
||||
loadImageWithCorrectOrientation()
|
||||
}
|
||||
}
|
||||
|
||||
private func loadImageWithCorrectOrientation() {
|
||||
Task {
|
||||
let correctedImage = await loadAndCorrectImage()
|
||||
await MainActor.run {
|
||||
self.uiImage = correctedImage
|
||||
self.isLoading = false
|
||||
self.hasFailed = correctedImage == nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadAndCorrectImage() async -> UIImage? {
|
||||
// Load image data from ImageManager
|
||||
guard
|
||||
let data = await MainActor.run(body: {
|
||||
ImageManager.shared.loadImageData(fromPath: imagePath)
|
||||
})
|
||||
else { return nil }
|
||||
|
||||
// Create UIImage from data
|
||||
guard let originalImage = UIImage(data: data) else { return nil }
|
||||
|
||||
// Apply orientation correction
|
||||
return correctImageOrientation(originalImage)
|
||||
}
|
||||
|
||||
/// Corrects the orientation of a UIImage based on its EXIF data
|
||||
private func correctImageOrientation(_ image: UIImage) -> UIImage {
|
||||
// If the image is already in the correct orientation, return as-is
|
||||
if image.imageOrientation == .up {
|
||||
return image
|
||||
}
|
||||
|
||||
// Calculate the proper transformation matrix
|
||||
var transform = CGAffineTransform.identity
|
||||
|
||||
switch image.imageOrientation {
|
||||
case .down, .downMirrored:
|
||||
transform = transform.translatedBy(x: image.size.width, y: image.size.height)
|
||||
transform = transform.rotated(by: .pi)
|
||||
|
||||
case .left, .leftMirrored:
|
||||
transform = transform.translatedBy(x: image.size.width, y: 0)
|
||||
transform = transform.rotated(by: .pi / 2)
|
||||
|
||||
case .right, .rightMirrored:
|
||||
transform = transform.translatedBy(x: 0, y: image.size.height)
|
||||
transform = transform.rotated(by: -.pi / 2)
|
||||
|
||||
case .up, .upMirrored:
|
||||
break
|
||||
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
|
||||
switch image.imageOrientation {
|
||||
case .upMirrored, .downMirrored:
|
||||
transform = transform.translatedBy(x: image.size.width, y: 0)
|
||||
transform = transform.scaledBy(x: -1, y: 1)
|
||||
|
||||
case .leftMirrored, .rightMirrored:
|
||||
transform = transform.translatedBy(x: image.size.height, y: 0)
|
||||
transform = transform.scaledBy(x: -1, y: 1)
|
||||
|
||||
case .up, .down, .left, .right:
|
||||
break
|
||||
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
|
||||
// Create a new image context and apply the transformation
|
||||
guard let cgImage = image.cgImage else { return image }
|
||||
|
||||
let context = CGContext(
|
||||
data: nil,
|
||||
width: Int(image.size.width),
|
||||
height: Int(image.size.height),
|
||||
bitsPerComponent: cgImage.bitsPerComponent,
|
||||
bytesPerRow: 0,
|
||||
space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
|
||||
bitmapInfo: cgImage.bitmapInfo.rawValue
|
||||
)
|
||||
|
||||
guard let ctx = context else { return image }
|
||||
|
||||
ctx.concatenate(transform)
|
||||
|
||||
switch image.imageOrientation {
|
||||
case .left, .leftMirrored, .right, .rightMirrored:
|
||||
ctx.draw(
|
||||
cgImage, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
|
||||
default:
|
||||
ctx.draw(
|
||||
cgImage, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
|
||||
}
|
||||
|
||||
guard let newCGImage = ctx.makeImage() else { return image }
|
||||
return UIImage(cgImage: newCGImage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience Extensions
|
||||
|
||||
extension OrientationAwareImage {
|
||||
/// Creates an orientation-aware image with fill content mode
|
||||
static func fill(imagePath: String) -> OrientationAwareImage {
|
||||
OrientationAwareImage(imagePath: imagePath, contentMode: .fill)
|
||||
}
|
||||
|
||||
/// Creates an orientation-aware image with fit content mode
|
||||
static func fit(imagePath: String) -> OrientationAwareImage {
|
||||
OrientationAwareImage(imagePath: imagePath, contentMode: .fit)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user