Target image byte size across platforms

This commit is contained in:
jack
2025-10-15 17:37:19 +01:00
committed by islam
parent 7f743d48fe
commit c29c7d83fe
+37 -15
View File
@@ -14,6 +14,7 @@ enum ImageUtilsError: Error {
enum ImageUtils { enum ImageUtils {
private static let compressionQuality: CGFloat = 0.85 private static let compressionQuality: CGFloat = 0.85
private static let targetImageBytes: Int = 60_000
static func processImage(at url: URL, maxDimension: CGFloat = 512) throws -> URL { static func processImage(at url: URL, maxDimension: CGFloat = 512) throws -> URL {
let data = try Data(contentsOf: url) let data = try Data(contentsOf: url)
@@ -29,9 +30,15 @@ enum ImageUtils {
#if os(iOS) #if os(iOS)
static func processImage(_ image: UIImage, maxDimension: CGFloat = 512) throws -> URL { static func processImage(_ image: UIImage, maxDimension: CGFloat = 512) throws -> URL {
let scaled = scaledImage(image, maxDimension: maxDimension) let scaled = scaledImage(image, maxDimension: maxDimension)
guard let jpegData = scaled.jpegData(compressionQuality: compressionQuality) else { var quality = compressionQuality
guard var jpegData = scaled.jpegData(compressionQuality: quality) else {
throw ImageUtilsError.encodingFailed throw ImageUtilsError.encodingFailed
} }
while jpegData.count > targetImageBytes && quality > 0.3 {
quality -= 0.1
guard let next = scaled.jpegData(compressionQuality: quality) else { break }
jpegData = next
}
let outputURL = try makeOutputURL() let outputURL = try makeOutputURL()
try jpegData.write(to: outputURL, options: .atomic) try jpegData.write(to: outputURL, options: .atomic)
return outputURL return outputURL
@@ -73,21 +80,17 @@ enum ImageUtils {
guard let cgImage = context.makeImage() else { guard let cgImage = context.makeImage() else {
throw ImageUtilsError.encodingFailed throw ImageUtilsError.encodingFailed
} }
var quality = compressionQuality
guard var jpegData = encodeJPEG(from: cgImage, quality: quality) else {
throw ImageUtilsError.encodingFailed
}
while jpegData.count > targetImageBytes && quality > 0.3 {
quality -= 0.1
guard let next = encodeJPEG(from: cgImage, quality: quality) else { break }
jpegData = next
}
let outputURL = try makeOutputURL() let outputURL = try makeOutputURL()
guard let destination = CGImageDestinationCreateWithURL(outputURL as CFURL, UTType.jpeg.identifier as CFString, 1, nil) else { try jpegData.write(to: outputURL, options: .atomic)
throw ImageUtilsError.encodingFailed
}
let options: [CFString: Any] = [
kCGImageDestinationLossyCompressionQuality: compressionQuality,
kCGImagePropertyExifDictionary: [:],
kCGImagePropertyTIFFDictionary: [:],
kCGImagePropertyIPTCDictionary: [:],
kCGImagePropertyOrientation: 1
]
CGImageDestinationAddImage(destination, cgImage, options as CFDictionary)
guard CGImageDestinationFinalize(destination) else {
throw ImageUtilsError.encodingFailed
}
return outputURL return outputURL
} }
@@ -106,6 +109,25 @@ enum ImageUtils {
scaledImage.unlockFocus() scaledImage.unlockFocus()
return scaledImage return scaledImage
} }
private static func encodeJPEG(from cgImage: CGImage, quality: CGFloat) -> Data? {
let data = CFDataCreateMutable(nil, 0)
guard let destination = CGImageDestinationCreateWithData(data, UTType.jpeg.identifier as CFString, 1, nil) else {
return nil
}
let options: [CFString: Any] = [
kCGImageDestinationLossyCompressionQuality: quality,
kCGImagePropertyExifDictionary: [:],
kCGImagePropertyTIFFDictionary: [:],
kCGImagePropertyIPTCDictionary: [:],
kCGImagePropertyOrientation: 1
]
CGImageDestinationAddImage(destination, cgImage, options as CFDictionary)
guard CGImageDestinationFinalize(destination) else {
return nil
}
return data as Data
}
#endif #endif
private static func makeOutputURL() throws -> URL { private static func makeOutputURL() throws -> URL {