TZImageCropManager.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //
  2. // TZImageCropManager.m
  3. // TZImagePickerController
  4. //
  5. // Created by 谭真 on 2016/12/5.
  6. // Copyright © 2016年 谭真. All rights reserved.
  7. //
  8. #import "TZImageCropManager.h"
  9. #import "UIView+Layout.h"
  10. #import <ImageIO/ImageIO.h>
  11. #import "TZImageManager.h"
  12. #import "TZImagePickerController.h"
  13. @implementation TZImageCropManager
  14. /// 裁剪框背景的处理
  15. + (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop {
  16. UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds];
  17. CAShapeLayer *layer = [CAShapeLayer layer];
  18. if (needCircleCrop) { // 圆形裁剪框
  19. [path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]];
  20. } else { // 矩形裁剪框
  21. [path appendPath:[UIBezierPath bezierPathWithRect:cropRect]];
  22. }
  23. layer.path = path.CGPath;
  24. layer.fillRule = kCAFillRuleEvenOdd;
  25. layer.fillColor = [[UIColor blackColor] CGColor];
  26. layer.opacity = 0.5;
  27. [view.layer addSublayer:layer];
  28. }
  29. /// 获得裁剪后的图片
  30. + (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView {
  31. CGAffineTransform transform = CGAffineTransformIdentity;
  32. // 平移的处理
  33. CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView];
  34. CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2);
  35. CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x;
  36. CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y);
  37. CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y);
  38. transform = CGAffineTransformTranslate(transform, translation.x, translation.y);
  39. // 缩放的处理
  40. transform = CGAffineTransformScale(transform, zoomScale, zoomScale);
  41. CGImageRef imageRef = [self newTransformedImage:transform
  42. sourceImage:imageView.image.CGImage
  43. sourceSize:imageView.image.size
  44. outputWidth:rect.size.width * [UIScreen mainScreen].scale
  45. cropSize:rect.size
  46. imageViewSize:imageView.frame.size];
  47. UIImage *cropedImage = [UIImage imageWithCGImage:imageRef];
  48. cropedImage = [[TZImageManager manager] fixOrientation:cropedImage];
  49. CGImageRelease(imageRef);
  50. return cropedImage;
  51. }
  52. + (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize {
  53. CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize];
  54. CGFloat aspect = cropSize.height/cropSize.width;
  55. CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect);
  56. CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source));
  57. CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
  58. CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height));
  59. CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height);
  60. uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0);
  61. uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0);
  62. CGContextConcatCTM(context, uiCoords);
  63. CGContextConcatCTM(context, transform);
  64. CGContextScaleCTM(context, 1.0, -1.0);
  65. CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source);
  66. CGImageRef resultRef = CGBitmapContextCreateImage(context);
  67. CGContextRelease(context);
  68. CGImageRelease(source);
  69. return resultRef;
  70. }
  71. + (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size {
  72. CGSize srcSize = size;
  73. CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
  74. CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  75. CGColorSpaceRelease(rgbColorSpace);
  76. CGContextSetInterpolationQuality(context, kCGInterpolationNone);
  77. CGContextTranslateCTM(context, size.width/2, size.height/2);
  78. CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source);
  79. CGImageRef resultRef = CGBitmapContextCreateImage(context);
  80. CGContextRelease(context);
  81. return resultRef;
  82. }
  83. /// 获取圆形图片
  84. + (UIImage *)circularClipImage:(UIImage *)image {
  85. UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
  86. CGContextRef ctx = UIGraphicsGetCurrentContext();
  87. CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
  88. CGContextAddEllipseInRect(ctx, rect);
  89. CGContextClip(ctx);
  90. [image drawInRect:rect];
  91. UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext();
  92. UIGraphicsEndImageContext();
  93. return circleImage;
  94. }
  95. @end
  96. @implementation UIImage (TZGif)
  97. + (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data {
  98. if (!data) {
  99. return nil;
  100. }
  101. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
  102. size_t count = CGImageSourceGetCount(source);
  103. UIImage *animatedImage;
  104. if (count <= 1) {
  105. animatedImage = [[UIImage alloc] initWithData:data];
  106. }
  107. else {
  108. // images数组过大时内存会飙升,在这里限制下最大count
  109. NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 50;
  110. NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1);
  111. NSMutableArray *images = [NSMutableArray array];
  112. NSTimeInterval duration = 0.0f;
  113. for (size_t i = 0; i < count; i+=interval) {
  114. CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
  115. if (!image) {
  116. continue;
  117. }
  118. duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3);
  119. [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
  120. CGImageRelease(image);
  121. }
  122. if (!duration) {
  123. duration = (1.0f / 10.0f) * count;
  124. }
  125. animatedImage = [UIImage animatedImageWithImages:images duration:duration];
  126. }
  127. CFRelease(source);
  128. return animatedImage;
  129. }
  130. + (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
  131. float frameDuration = 0.1f;
  132. CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
  133. NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
  134. NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
  135. NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
  136. if (delayTimeUnclampedProp) {
  137. frameDuration = [delayTimeUnclampedProp floatValue];
  138. }
  139. else {
  140. NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
  141. if (delayTimeProp) {
  142. frameDuration = [delayTimeProp floatValue];
  143. }
  144. }
  145. // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
  146. // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
  147. // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
  148. // for more information.
  149. if (frameDuration < 0.011f) {
  150. frameDuration = 0.100f;
  151. }
  152. CFRelease(cfFrameProperties);
  153. return frameDuration;
  154. }
  155. @end