@ -17,6 +17,7 @@ enum SignalAttachmentError: Error {
case couldNotConvertToMpeg4
case couldNotRemoveMetadata
case invalidFileFormat
case couldNotResizeImage
}
extension String {
@ -56,6 +57,8 @@ extension SignalAttachmentError: LocalizedError {
return NSLocalizedString ( " ATTACHMENT_ERROR_COULD_NOT_CONVERT_TO_MP4 " , comment : " Attachment error message for video attachments which could not be converted to MP4 " )
case . couldNotRemoveMetadata :
return NSLocalizedString ( " ATTACHMENT_ERROR_COULD_NOT_REMOVE_METADATA " , comment : " Attachment error message for image attachments in which metadata could not be removed " )
case . couldNotResizeImage :
return NSLocalizedString ( " ATTACHMENT_ERROR_COULD_NOT_RESIZE_IMAGE " , comment : " Attachment error message for image attachments which could not be resized " )
}
}
}
@ -700,7 +703,11 @@ public class SignalAttachment: NSObject {
var dstImage : UIImage ! = image
if image . size . width > maxSize ||
image . size . height > maxSize {
dstImage = imageScaled ( image , toMaxSize : maxSize )
guard let resizedImage = imageScaled ( image , toMaxSize : maxSize ) else {
attachment . error = . couldNotResizeImage
return attachment
}
dstImage = resizedImage
}
guard let jpgImageData = UIImageJPEGRepresentation ( dstImage ,
jpegCompressionQuality ( imageUploadQuality : imageUploadQuality ) ) else {
@ -746,20 +753,56 @@ public class SignalAttachment: NSObject {
}
}
private class func imageScaled ( _ image : UIImage , toMaxSize size : CGFloat ) -> UIImage {
var scaleFactor : CGFloat
let aspectRatio : CGFloat = image . size . height / image . size . width
if aspectRatio > 1 {
scaleFactor = size / image . size . width
} else {
scaleFactor = size / image . size . height
}
let newSize = CGSize ( width : CGFloat ( image . size . width * scaleFactor ) , height : CGFloat ( image . size . height * scaleFactor ) )
UIGraphicsBeginImageContext ( newSize )
image . draw ( in : CGRect ( x : CGFloat ( 0 ) , y : CGFloat ( 0 ) , width : CGFloat ( newSize . width ) , height : CGFloat ( newSize . height ) ) )
let updatedImage : UIImage ? = UIGraphicsGetImageFromCurrentImageContext ( )
UIGraphicsEndImageContext ( )
return updatedImage !
// N O T E : F o r u n k n o w n r e a s o n s , r e s i z i n g i m a g e s w i t h U I G r a p h i c s B e g i n I m a g e C o n t e x t ( )
// c r a s h e s r e l i a b l y i n t h e s h a r e e x t e n s i o n a f t e r s c r e e n l o c k ' s a u t h U I h a s b e e n p r e s e n t e d .
// R e s i z i n g u s i n g a C G C o n t e x t s e e m s t o w o r k f i n e .
private class func imageScaled ( _ uiImage : UIImage , toMaxSize maxSize : CGFloat ) -> UIImage ? {
guard let cgImage = uiImage . cgImage else {
owsFail ( " \( logTag ) UIImage missing cgImage. " )
return nil
}
// I t ' s e s s e n t i a l t h a t w e w o r k c o n s i s t e n t l y i n " C G " c o o r d i n a t e s ( w h i c h a r e
// p i x e l s a n d d o n ' t r e f l e c t o r i e n t a t i o n ) , n o t " U I " c o o r d i n a t e s ( w h i c h
// a r e p o i n t s a n d d o r e f l e c t o r i e n t a t i o n ) .
let scrSize = CGSize ( width : cgImage . width , height : cgImage . height )
var maxSizeRect = CGRect . zero
maxSizeRect . size = CGSize ( width : maxSize , height : maxSize )
let newSize = AVMakeRect ( aspectRatio : scrSize , insideRect : maxSizeRect ) . size
assert ( newSize . width <= maxSize )
assert ( newSize . height <= maxSize )
let bitsPerComponent = cgImage . bitsPerComponent
let bytesPerRow = cgImage . bytesPerRow
guard let colorSpace = cgImage . colorSpace else {
owsFail ( " \( logTag ) cgImage missing colorSpace. " )
return nil
}
let bitmapInfo = cgImage . bitmapInfo
guard let context = CGContext . init ( data : nil ,
width : Int ( newSize . width ) ,
height : Int ( newSize . height ) ,
bitsPerComponent : bitsPerComponent ,
bytesPerRow : bytesPerRow ,
space : colorSpace ,
bitmapInfo : bitmapInfo . rawValue ) else {
owsFail ( " \( logTag ) could not create CGContext. " )
return nil
}
context . interpolationQuality = . high
var drawRect = CGRect . zero
drawRect . size = newSize
context . draw ( cgImage , in : drawRect )
guard let newCGImage = context . makeImage ( ) else {
owsFail ( " \( logTag ) could not create new CGImage. " )
return nil
}
return UIImage ( cgImage : newCGImage ,
scale : uiImage . scale ,
orientation : uiImage . imageOrientation )
}
private class func doesImageHaveAcceptableFileSize ( dataSource : DataSource , imageQuality : TSImageQuality ) -> Bool {