diff --git a/mobile/ios/Runner/Images/LocalImagesImpl.swift b/mobile/ios/Runner/Images/LocalImagesImpl.swift index 7ccae1531d..0d023ad466 100644 --- a/mobile/ios/Runner/Images/LocalImagesImpl.swift +++ b/mobile/ios/Runner/Images/LocalImagesImpl.swift @@ -20,7 +20,7 @@ class LocalImageApiImpl: LocalImageApi { requestOptions.version = .current return requestOptions }() - private static let maxOriginalPixelSize: CGFloat = 16384 + private static let maxPixelSize: CGFloat = 16384 private static let registry = RequestRegistry() @@ -109,18 +109,16 @@ class LocalImageApiImpl: LocalImageApi { ])) } - // PHImageManagerMaximumSize returns a distorted aspect ratio for images too large to render - // (extreme panoramas / long screenshots). Bound the long edge to 16384 (the GPU max texture - // size) with aspectFit so the true ratio is kept and the buffer stays renderable. .fast resize - // is enough, we only need the long edge under the limit. let isOriginal = !(width > 0 && height > 0) + let targetSize = isOriginal + ? CGSize(width: Self.maxPixelSize, height: Self.maxPixelSize) + : CGSize(width: Double(width), height: Double(height)) + let contentMode: PHImageContentMode = isOriginal ? .aspectFit : .aspectFill var image: UIImage? Self.imageManager.requestImage( for: asset, - targetSize: isOriginal - ? CGSize(width: Self.maxOriginalPixelSize, height: Self.maxOriginalPixelSize) - : CGSize(width: Double(width), height: Double(height)), - contentMode: isOriginal ? .aspectFit : .aspectFill, + targetSize: targetSize, + contentMode: contentMode, options: Self.requestOptions, resultHandler: { (_image, info) -> Void in image = _image @@ -131,12 +129,38 @@ class LocalImageApiImpl: LocalImageApi { return request.completion(ImageProcessing.cancelledResult) } - guard let image = image, - let cgImage = image.cgImage else { + guard let fastImage = image, + var cgImage = fastImage.cgImage else { Self.registry.remove(requestId: requestId) return request.completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil))) } + // .fast can return larger than the target, so retry with .exact to guarantee the bound. + if max(cgImage.width, cgImage.height) > Int(Self.maxPixelSize) { + let exactOptions = Self.requestOptions.copy() as! PHImageRequestOptions + exactOptions.resizeMode = .exact + image = nil + Self.imageManager.requestImage( + for: asset, + targetSize: targetSize, + contentMode: contentMode, + options: exactOptions, + resultHandler: { (_image, info) -> Void in + image = _image + } + ) + + if request.isCancelled { + return request.completion(ImageProcessing.cancelledResult) + } + + guard let exactImage = image?.cgImage else { + Self.registry.remove(requestId: requestId) + return request.completion(.failure(PigeonError(code: "", message: "Could not resize image for \(assetId)", details: nil))) + } + cgImage = exactImage + } + if request.isCancelled { return request.completion(ImageProcessing.cancelledResult) }