UIImage+YMCameraManager.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. //
  2. // UIImage+YMCameraManager.m
  3. // MSYOUPAI
  4. //
  5. // Created by YoMi on 2024/3/17.
  6. // Copyright © 2024 MS. All rights reserved.
  7. //
  8. #import "UIImage+YMCameraManager.h"
  9. @implementation UIImage (YMCameraManager)
  10. // This method ignores the image's imageOrientation setting.
  11. - (UIImage *)croppedImage:(CGRect)bounds {
  12. CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds);
  13. UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
  14. CGImageRelease(imageRef);
  15. return croppedImage;
  16. }
  17. - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
  18. BOOL drawTransposed;
  19. CGAffineTransform transform = CGAffineTransformIdentity;
  20. if([[[UIDevice currentDevice]systemVersion]floatValue] >= 5.0) {
  21. drawTransposed = YES;
  22. }
  23. else {
  24. switch(self.imageOrientation) {
  25. case UIImageOrientationLeft:
  26. case UIImageOrientationLeftMirrored:
  27. case UIImageOrientationRight:
  28. case UIImageOrientationRightMirrored:
  29. drawTransposed = YES;
  30. break;
  31. default:
  32. drawTransposed = NO;
  33. }
  34. transform = [self transformForOrientation:newSize];
  35. }
  36. transform = [self transformForOrientation:newSize];
  37. return [self resizedImage:newSize transform:transform drawTransposed:drawTransposed interpolationQuality:quality];
  38. }
  39. // Resizes the image according to the given content mode, taking into account the image's orientation
  40. - (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode
  41. bounds:(CGSize)bounds
  42. interpolationQuality:(CGInterpolationQuality)quality {
  43. CGFloat horizontalRatio = bounds.width / self.size.width;
  44. CGFloat verticalRatio = bounds.height / self.size.height;
  45. CGFloat ratio;
  46. switch(contentMode) {
  47. case UIViewContentModeScaleAspectFill:
  48. ratio = MAX(horizontalRatio, verticalRatio);
  49. break;
  50. case UIViewContentModeScaleAspectFit:
  51. ratio = MIN(horizontalRatio, verticalRatio);
  52. break;
  53. default:
  54. [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %ld", (long)contentMode];
  55. }
  56. CGSize newSize = CGSizeMake(self.size.width * ratio, self.size.height * ratio);
  57. return [self resizedImage:newSize interpolationQuality:quality];
  58. }
  59. #pragma mark - fix orientation
  60. - (UIImage *)fixOrientation {
  61. // No-op if the orientation is already correct
  62. if (self.imageOrientation == UIImageOrientationUp) return self;
  63. // We need to calculate the proper transformation to make the image upright.
  64. // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
  65. CGAffineTransform transform = CGAffineTransformIdentity;
  66. switch (self.imageOrientation) {
  67. case UIImageOrientationDown:
  68. case UIImageOrientationDownMirrored:
  69. transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
  70. transform = CGAffineTransformRotate(transform, M_PI);
  71. break;
  72. case UIImageOrientationLeft:
  73. case UIImageOrientationLeftMirrored:
  74. transform = CGAffineTransformTranslate(transform, self.size.width, 0);
  75. transform = CGAffineTransformRotate(transform, M_PI_2);
  76. break;
  77. case UIImageOrientationRight:
  78. case UIImageOrientationRightMirrored:
  79. transform = CGAffineTransformTranslate(transform, 0, self.size.height);
  80. transform = CGAffineTransformRotate(transform, -M_PI_2);
  81. break;
  82. case UIImageOrientationUp:
  83. case UIImageOrientationUpMirrored:
  84. break;
  85. }
  86. switch (self.imageOrientation) {
  87. case UIImageOrientationUpMirrored:
  88. case UIImageOrientationDownMirrored:
  89. transform = CGAffineTransformTranslate(transform, self.size.width, 0);
  90. transform = CGAffineTransformScale(transform, -1, 1);
  91. break;
  92. case UIImageOrientationLeftMirrored:
  93. case UIImageOrientationRightMirrored:
  94. transform = CGAffineTransformTranslate(transform, self.size.height, 0);
  95. transform = CGAffineTransformScale(transform, -1, 1);
  96. break;
  97. case UIImageOrientationUp:
  98. case UIImageOrientationDown:
  99. case UIImageOrientationLeft:
  100. case UIImageOrientationRight:
  101. break;
  102. }
  103. // Now we draw the underlying CGImage into a new context, applying the transform
  104. // calculated above.
  105. CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
  106. CGImageGetBitsPerComponent(self.CGImage), 0,
  107. CGImageGetColorSpace(self.CGImage),
  108. CGImageGetBitmapInfo(self.CGImage));
  109. CGContextConcatCTM(ctx, transform);
  110. switch (self.imageOrientation) {
  111. case UIImageOrientationLeft:
  112. case UIImageOrientationLeftMirrored:
  113. case UIImageOrientationRight:
  114. case UIImageOrientationRightMirrored:
  115. // Grr...
  116. CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
  117. break;
  118. default:
  119. CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
  120. break;
  121. }
  122. // And now we just create a new UIImage from the drawing context
  123. CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
  124. UIImage *img = [UIImage imageWithCGImage:cgimg];
  125. CGContextRelease(ctx);
  126. CGImageRelease(cgimg);
  127. return img;
  128. }
  129. - (UIImage *)rotatedByDegrees:(CGFloat)degrees
  130. {
  131. // calculate the size of the rotated view's containing box for our drawing space
  132. UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
  133. CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
  134. rotatedViewBox.transform = t;
  135. CGSize rotatedSize = rotatedViewBox.frame.size;
  136. // 传入的View.frame.size是0的话,直接返回nil,防止 UIGraphicsBeginImageContext() 传入0,导致崩溃
  137. if (CGSizeEqualToSize(rotatedSize, CGSizeZero)) {
  138. return nil;
  139. }
  140. // Create the bitmap context
  141. UIGraphicsBeginImageContext(rotatedSize);
  142. CGContextRef bitmap = UIGraphicsGetCurrentContext();
  143. // Move the origin to the middle of the image so we will rotate and scale around the center.
  144. CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
  145. // // Rotate the image context
  146. CGContextRotateCTM(bitmap, DegreesToRadians(degrees));
  147. // Now, draw the rotated/scaled image into the context
  148. CGContextScaleCTM(bitmap, 1.0, -1.0);
  149. CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
  150. UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  151. UIGraphicsEndImageContext();
  152. return newImage;
  153. }
  154. #pragma mark - Private helper methods
  155. // Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size
  156. // The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation
  157. // If the new size is not integral, it will be rounded up
  158. - (UIImage *)resizedImage:(CGSize)newSize
  159. transform:(CGAffineTransform)transform
  160. drawTransposed:(BOOL)transpose
  161. interpolationQuality:(CGInterpolationQuality)quality {
  162. CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
  163. CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width);
  164. CGImageRef imageRef = self.CGImage;
  165. // Fix for a colorspace / transparency issue that affects some types of
  166. // images. See here: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/comment-page-2/#comment-39951
  167. CGContextRef bitmap = CGBitmapContextCreate(NULL,
  168. newRect.size.width,
  169. newRect.size.height,
  170. 8,
  171. 0,
  172. CGImageGetColorSpace(imageRef),
  173. kCGImageAlphaNoneSkipLast);
  174. // Rotate and/or flip the image if required by its orientation
  175. CGContextConcatCTM(bitmap, transform);
  176. // Set the quality level to use when rescaling
  177. CGContextSetInterpolationQuality(bitmap, quality);
  178. // Draw into the context; this scales the image
  179. CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef);
  180. // Get the resized image from the context and a UIImage
  181. CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
  182. UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
  183. // Clean up
  184. CGContextRelease(bitmap);
  185. CGImageRelease(newImageRef);
  186. return newImage;
  187. }
  188. // Returns an affine transform that takes into account the image orientation when drawing a scaled image
  189. - (CGAffineTransform)transformForOrientation:(CGSize)newSize {
  190. CGAffineTransform transform = CGAffineTransformIdentity;
  191. switch(self.imageOrientation) {
  192. case UIImageOrientationDown: // EXIF = 3
  193. case UIImageOrientationDownMirrored: // EXIF = 4
  194. transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height);
  195. transform = CGAffineTransformRotate(transform, M_PI);
  196. break;
  197. case UIImageOrientationLeft: // EXIF = 6
  198. case UIImageOrientationLeftMirrored: // EXIF = 5
  199. transform = CGAffineTransformTranslate(transform, newSize.width, 0);
  200. transform = CGAffineTransformRotate(transform, M_PI_2);
  201. break;
  202. case UIImageOrientationRight: // EXIF = 8
  203. case UIImageOrientationRightMirrored: // EXIF = 7
  204. transform = CGAffineTransformTranslate(transform, 0, newSize.height);
  205. transform = CGAffineTransformRotate(transform, -M_PI_2);
  206. break;
  207. default:
  208. break;
  209. }
  210. switch(self.imageOrientation) {
  211. case UIImageOrientationUpMirrored: // EXIF = 2
  212. case UIImageOrientationDownMirrored: // EXIF = 4
  213. transform = CGAffineTransformTranslate(transform, newSize.width, 0);
  214. transform = CGAffineTransformScale(transform, -1, 1);
  215. break;
  216. case UIImageOrientationLeftMirrored: // EXIF = 5
  217. case UIImageOrientationRightMirrored: // EXIF = 7
  218. transform = CGAffineTransformTranslate(transform, newSize.height, 0);
  219. transform = CGAffineTransformScale(transform, -1, 1);
  220. break;
  221. default:
  222. break;
  223. }
  224. return transform;
  225. }
  226. @end