New file |
| | |
| | | // |
| | | // TZImageCropManager.m |
| | | // TZImagePickerController |
| | | // |
| | | // Created by 谭真 on 2016/12/5. |
| | | // Copyright © 2016年 谭真. All rights reserved. |
| | | // |
| | | |
| | | #import "TZImageCropManager.h" |
| | | #import "UIView+Layout.h" |
| | | #import <ImageIO/ImageIO.h> |
| | | #import "TZImageManager.h" |
| | | #import "TZImagePickerController.h" |
| | | |
| | | @implementation TZImageCropManager |
| | | |
| | | /// 裁剪框背景的处理 |
| | | + (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop { |
| | | UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds]; |
| | | CAShapeLayer *layer = [CAShapeLayer layer]; |
| | | if (needCircleCrop) { // 圆形裁剪框 |
| | | [path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]]; |
| | | } else { // 矩形裁剪框 |
| | | [path appendPath:[UIBezierPath bezierPathWithRect:cropRect]]; |
| | | } |
| | | layer.path = path.CGPath; |
| | | layer.fillRule = kCAFillRuleEvenOdd; |
| | | layer.fillColor = [[UIColor blackColor] CGColor]; |
| | | layer.opacity = 0.5; |
| | | [view.layer addSublayer:layer]; |
| | | } |
| | | |
| | | /// 获得裁剪后的图片 |
| | | + (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView { |
| | | CGAffineTransform transform = CGAffineTransformIdentity; |
| | | // 平移的处理 |
| | | CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView]; |
| | | CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2); |
| | | CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x; |
| | | CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y); |
| | | CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y); |
| | | transform = CGAffineTransformTranslate(transform, translation.x, translation.y); |
| | | // 缩放的处理 |
| | | transform = CGAffineTransformScale(transform, zoomScale, zoomScale); |
| | | |
| | | CGImageRef imageRef = [self newTransformedImage:transform |
| | | sourceImage:imageView.image.CGImage |
| | | sourceSize:imageView.image.size |
| | | outputWidth:rect.size.width * [UIScreen mainScreen].scale |
| | | cropSize:rect.size |
| | | imageViewSize:imageView.frame.size]; |
| | | UIImage *cropedImage = [UIImage imageWithCGImage:imageRef]; |
| | | cropedImage = [[TZImageManager manager] fixOrientation:cropedImage]; |
| | | CGImageRelease(imageRef); |
| | | return cropedImage; |
| | | } |
| | | |
| | | + (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize { |
| | | CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize]; |
| | | |
| | | CGFloat aspect = cropSize.height/cropSize.width; |
| | | CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect); |
| | | |
| | | CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source)); |
| | | CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); |
| | | CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height)); |
| | | |
| | | CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height); |
| | | uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0); |
| | | uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0); |
| | | CGContextConcatCTM(context, uiCoords); |
| | | |
| | | CGContextConcatCTM(context, transform); |
| | | CGContextScaleCTM(context, 1.0, -1.0); |
| | | |
| | | CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source); |
| | | CGImageRef resultRef = CGBitmapContextCreateImage(context); |
| | | CGContextRelease(context); |
| | | CGImageRelease(source); |
| | | return resultRef; |
| | | } |
| | | |
| | | + (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size { |
| | | CGSize srcSize = size; |
| | | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); |
| | | CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); |
| | | CGColorSpaceRelease(rgbColorSpace); |
| | | |
| | | CGContextSetInterpolationQuality(context, kCGInterpolationNone); |
| | | CGContextTranslateCTM(context, size.width/2, size.height/2); |
| | | |
| | | CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source); |
| | | |
| | | CGImageRef resultRef = CGBitmapContextCreateImage(context); |
| | | CGContextRelease(context); |
| | | return resultRef; |
| | | } |
| | | |
| | | /// 获取圆形图片 |
| | | + (UIImage *)circularClipImage:(UIImage *)image { |
| | | UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale); |
| | | |
| | | CGContextRef ctx = UIGraphicsGetCurrentContext(); |
| | | CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); |
| | | CGContextAddEllipseInRect(ctx, rect); |
| | | CGContextClip(ctx); |
| | | |
| | | [image drawInRect:rect]; |
| | | UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext(); |
| | | |
| | | UIGraphicsEndImageContext(); |
| | | return circleImage; |
| | | } |
| | | |
| | | @end |
| | | |
| | | |
| | | @implementation UIImage (TZGif) |
| | | |
| | | + (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data { |
| | | if (!data) { |
| | | return nil; |
| | | } |
| | | |
| | | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); |
| | | |
| | | size_t count = CGImageSourceGetCount(source); |
| | | |
| | | UIImage *animatedImage; |
| | | |
| | | if (count <= 1) { |
| | | animatedImage = [[UIImage alloc] initWithData:data]; |
| | | } |
| | | else { |
| | | // images数组过大时内存会飙升,在这里限制下最大count |
| | | NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 200; |
| | | NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1); |
| | | |
| | | NSMutableArray *images = [NSMutableArray array]; |
| | | |
| | | NSTimeInterval duration = 0.0f; |
| | | |
| | | for (size_t i = 0; i < count; i+=interval) { |
| | | CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); |
| | | if (!image) { |
| | | continue; |
| | | } |
| | | |
| | | duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3); |
| | | |
| | | [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; |
| | | |
| | | CGImageRelease(image); |
| | | } |
| | | |
| | | if (!duration) { |
| | | duration = (1.0f / 10.0f) * count; |
| | | } |
| | | |
| | | animatedImage = [UIImage animatedImageWithImages:images duration:duration]; |
| | | } |
| | | |
| | | CFRelease(source); |
| | | |
| | | return animatedImage; |
| | | } |
| | | |
| | | + (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { |
| | | float frameDuration = 0.1f; |
| | | CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); |
| | | NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; |
| | | NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; |
| | | |
| | | NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; |
| | | if (delayTimeUnclampedProp) { |
| | | frameDuration = [delayTimeUnclampedProp floatValue]; |
| | | } |
| | | else { |
| | | |
| | | NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; |
| | | if (delayTimeProp) { |
| | | frameDuration = [delayTimeProp floatValue]; |
| | | } |
| | | } |
| | | |
| | | // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. |
| | | // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify |
| | | // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> |
| | | // for more information. |
| | | |
| | | if (frameDuration < 0.011f) { |
| | | frameDuration = 0.100f; |
| | | } |
| | | |
| | | CFRelease(cfFrameProperties); |
| | | return frameDuration; |
| | | } |
| | | |
| | | @end |