UIImage+Extension.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. //
  2. // UIImage+Extension.m
  3. // knowledgeBase
  4. //
  5. // Created by 王洪亮 on 16/9/20.
  6. // Copyright © 2016年 wanghongliang. All rights reserved.
  7. //
  8. #import "UIImage+Extension.h"
  9. #ifndef YY_SWAP // swap two value
  10. #define YY_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0)
  11. #endif
  12. @implementation UIImage (Extension)
  13. + (UIImage *)imageWithBgColor:(UIColor *)color alpha:(CGFloat)alpha{
  14. UIImage *transparentBackground;
  15. UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, [UINavigationBar appearance].layer.contentsScale);
  16. CGContextRef context = UIGraphicsGetCurrentContext();
  17. CGContextSetFillColorWithColor(context, [[color colorWithAlphaComponent:alpha] CGColor]);
  18. UIRectFill(CGRectMake(0, 0, 1, 1));
  19. transparentBackground = UIGraphicsGetImageFromCurrentImageContext();
  20. UIGraphicsEndImageContext();
  21. return transparentBackground;
  22. }
  23. - (UIImage *)horTransform{
  24. //原始图片
  25. UIImage *srcImage =self;
  26. //Quartz重绘图片
  27. CGRect rect= CGRectMake(0, 0, srcImage.size.width, srcImage.size.height);
  28. // let rect = CGRectMake(0, 0, srcImage.size.width , srcImage.size.height);//创建矩形框
  29. //根据size大小创建一个基于位图的图形上下文
  30. UIGraphicsBeginImageContextWithOptions(rect.size, false, 2);
  31. CGContextRef currentContext = UIGraphicsGetCurrentContext();//获取当前quartz 2d绘图环境
  32. CGContextClipToRect(currentContext, rect);//设置当前绘图环境到矩形框
  33. CGContextRotateCTM(currentContext,M_PI); //旋转180度
  34. //平移, 这里是平移坐标系,跟平移图形是一个道理
  35. CGContextTranslateCTM(currentContext, -rect.size.width, -rect.size.height);
  36. CGContextDrawImage(currentContext, rect, srcImage.CGImage);//绘图
  37. //翻转图片
  38. UIImage *drawImage = UIGraphicsGetImageFromCurrentImageContext();//获得图片
  39. UIImage *flipImage = [UIImage imageWithCGImage:drawImage.CGImage scale:srcImage.scale orientation:srcImage.imageOrientation];
  40. return flipImage;
  41. }
  42. - (UIImage*)imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{
  43. CGRect rect = CGRectMake(0, 0, size.width, size.height);
  44. UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
  45. CGContextRef ctx = UIGraphicsGetCurrentContext();
  46. UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
  47. CGContextAddPath(ctx,path.CGPath);
  48. CGContextClip(ctx);
  49. [self drawInRect:rect];
  50. CGContextDrawPath(ctx, kCGPathFillStroke);
  51. UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
  52. UIGraphicsEndImageContext();
  53. return newImage;
  54. }
  55. + (UIImage *)boxblurImageWithBlur:(CGFloat)blur andImage:(UIImage *)image
  56. {
  57. NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg
  58. UIImage* destImage = [UIImage imageWithData:imageData];
  59. if (blur < 0.f || blur > 1.f) {
  60. blur = 0.5f;
  61. }
  62. int boxSize = (int)(blur * 40);
  63. boxSize = boxSize - (boxSize % 2) + 1;
  64. CGImageRef img = destImage.CGImage;
  65. vImage_Buffer inBuffer, outBuffer;
  66. vImage_Error error;
  67. void *pixelBuffer;
  68. //create vImage_Buffer with data from CGImageRef
  69. CGDataProviderRef inProvider = CGImageGetDataProvider(img);
  70. CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
  71. inBuffer.width = CGImageGetWidth(img);
  72. inBuffer.height = CGImageGetHeight(img);
  73. inBuffer.rowBytes = CGImageGetBytesPerRow(img);
  74. inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
  75. //create vImage_Buffer for output
  76. pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
  77. if(pixelBuffer == NULL)
  78. NSLog(@"No pixelbuffer");
  79. outBuffer.data = pixelBuffer;
  80. outBuffer.width = CGImageGetWidth(img);
  81. outBuffer.height = CGImageGetHeight(img);
  82. outBuffer.rowBytes = CGImageGetBytesPerRow(img);
  83. // Create a third buffer for intermediate processing
  84. void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
  85. vImage_Buffer outBuffer2;
  86. outBuffer2.data = pixelBuffer2;
  87. outBuffer2.width = CGImageGetWidth(img);
  88. outBuffer2.height = CGImageGetHeight(img);
  89. outBuffer2.rowBytes = CGImageGetBytesPerRow(img);
  90. //perform convolution
  91. error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  92. if (error) {
  93. NSLog(@"error from convolution %ld", error);
  94. }
  95. error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  96. if (error) {
  97. NSLog(@"error from convolution %ld", error);
  98. }
  99. error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
  100. if (error) {
  101. NSLog(@"error from convolution %ld", error);
  102. }
  103. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  104. CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
  105. outBuffer.width,
  106. outBuffer.height,
  107. 8,
  108. outBuffer.rowBytes,
  109. colorSpace,
  110. (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
  111. CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
  112. UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
  113. //clean up
  114. CGContextRelease(ctx);
  115. CGColorSpaceRelease(colorSpace);
  116. free(pixelBuffer);
  117. free(pixelBuffer2);
  118. CFRelease(inBitmapData);
  119. CGImageRelease(imageRef);
  120. return returnImage;
  121. }
  122. - (UIImage *)imageByBlurRadius:(CGFloat)blurRadius
  123. tintColor:(UIColor *)tintColor
  124. tintMode:(CGBlendMode)tintBlendMode
  125. saturation:(CGFloat)saturation
  126. maskImage:(UIImage *)maskImage {
  127. if (self.size.width < 1 || self.size.height < 1) {
  128. NSLog(@"UIImage+YYAdd error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
  129. return nil;
  130. }
  131. if (!self.CGImage) {
  132. NSLog(@"UIImage+YYAdd error: inputImage must be backed by a CGImage: %@", self);
  133. return nil;
  134. }
  135. if (maskImage && !maskImage.CGImage) {
  136. NSLog(@"UIImage+YYAdd error: effectMaskImage must be backed by a CGImage: %@", maskImage);
  137. return nil;
  138. }
  139. // iOS7 and above can use new func.
  140. BOOL hasNewFunc = (long)vImageBuffer_InitWithCGImage != 0 && (long)vImageCreateCGImageFromBuffer != 0;
  141. BOOL hasBlur = blurRadius > __FLT_EPSILON__;
  142. BOOL hasSaturation = fabs(saturation - 1.0) > __FLT_EPSILON__;
  143. CGSize size = self.size;
  144. CGRect rect = { CGPointZero, size };
  145. CGFloat scale = self.scale;
  146. CGImageRef imageRef = self.CGImage;
  147. BOOL opaque = NO;
  148. if (!hasBlur && !hasSaturation) {
  149. return [self _yy_mergeImageRef:imageRef tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
  150. }
  151. vImage_Buffer effect = { 0 }, scratch = { 0 };
  152. vImage_Buffer *input = NULL, *output = NULL;
  153. vImage_CGImageFormat format = {
  154. .bitsPerComponent = 8,
  155. .bitsPerPixel = 32,
  156. .colorSpace = NULL,
  157. .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, //requests a BGRA buffer.
  158. .version = 0,
  159. .decode = NULL,
  160. .renderingIntent = kCGRenderingIntentDefault
  161. };
  162. if (hasNewFunc) {
  163. vImage_Error err;
  164. err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImagePrintDiagnosticsToConsole);
  165. if (err != kvImageNoError) {
  166. NSLog(@"UIImage+YYAdd error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self);
  167. return nil;
  168. }
  169. err = vImageBuffer_Init(&scratch, effect.height, effect.width, format.bitsPerPixel, kvImageNoFlags);
  170. if (err != kvImageNoError) {
  171. NSLog(@"UIImage+YYAdd error: vImageBuffer_Init returned error code %zi for inputImage: %@", err, self);
  172. return nil;
  173. }
  174. } else {
  175. UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
  176. CGContextRef effectCtx = UIGraphicsGetCurrentContext();
  177. CGContextScaleCTM(effectCtx, 1.0, -1.0);
  178. CGContextTranslateCTM(effectCtx, 0, -size.height);
  179. CGContextDrawImage(effectCtx, rect, imageRef);
  180. effect.data = CGBitmapContextGetData(effectCtx);
  181. effect.width = CGBitmapContextGetWidth(effectCtx);
  182. effect.height = CGBitmapContextGetHeight(effectCtx);
  183. effect.rowBytes = CGBitmapContextGetBytesPerRow(effectCtx);
  184. UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
  185. CGContextRef scratchCtx = UIGraphicsGetCurrentContext();
  186. scratch.data = CGBitmapContextGetData(scratchCtx);
  187. scratch.width = CGBitmapContextGetWidth(scratchCtx);
  188. scratch.height = CGBitmapContextGetHeight(scratchCtx);
  189. scratch.rowBytes = CGBitmapContextGetBytesPerRow(scratchCtx);
  190. }
  191. input = &effect;
  192. output = &scratch;
  193. if (hasBlur) {
  194. // A description of how to compute the box kernel width from the Gaussian
  195. // radius (aka standard deviation) appears in the SVG spec:
  196. // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
  197. //
  198. // For larger values of 's' (s >= 2.0), an approximation can be used: Three
  199. // successive box-blurs build a piece-wise quadratic convolution kernel, which
  200. // approximates the Gaussian kernel to within roughly 3%.
  201. //
  202. // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
  203. //
  204. // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
  205. //
  206. CGFloat inputRadius = blurRadius * scale;
  207. if (inputRadius - 2.0 < __FLT_EPSILON__) inputRadius = 2.0;
  208. uint32_t radius = floor((inputRadius * 3.0 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
  209. radius |= 1; // force radius to be odd so that the three box-blur methodology works.
  210. int iterations;
  211. if (blurRadius * scale < 0.5) iterations = 1;
  212. else if (blurRadius * scale < 1.5) iterations = 2;
  213. else iterations = 3;
  214. NSInteger tempSize = vImageBoxConvolve_ARGB8888(input, output, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
  215. void *temp = malloc(tempSize);
  216. for (int i = 0; i < iterations; i++) {
  217. vImageBoxConvolve_ARGB8888(input, output, temp, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
  218. YY_SWAP(input, output);
  219. }
  220. free(temp);
  221. }
  222. if (hasSaturation) {
  223. // These values appear in the W3C Filter Effects spec:
  224. // https://dvcs.w3.org/hg/FXTF/raw-file/default/filters/Publish.html#grayscaleEquivalent
  225. CGFloat s = saturation;
  226. CGFloat matrixFloat[] = {
  227. 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
  228. 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
  229. 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
  230. 0, 0, 0, 1,
  231. };
  232. const int32_t divisor = 256;
  233. NSUInteger matrixSize = sizeof(matrixFloat) / sizeof(matrixFloat[0]);
  234. int16_t matrix[matrixSize];
  235. for (NSUInteger i = 0; i < matrixSize; ++i) {
  236. matrix[i] = (int16_t)roundf(matrixFloat[i] * divisor);
  237. }
  238. vImageMatrixMultiply_ARGB8888(input, output, matrix, divisor, NULL, NULL, kvImageNoFlags);
  239. YY_SWAP(input, output);
  240. }
  241. UIImage *outputImage = nil;
  242. if (hasNewFunc) {
  243. CGImageRef effectCGImage = NULL;
  244. effectCGImage = vImageCreateCGImageFromBuffer(input, &format, &_yy_cleanupBuffer, NULL, kvImageNoAllocate, NULL);
  245. if (effectCGImage == NULL) {
  246. effectCGImage = vImageCreateCGImageFromBuffer(input, &format, NULL, NULL, kvImageNoFlags, NULL);
  247. free(input->data);
  248. }
  249. free(output->data);
  250. outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
  251. CGImageRelease(effectCGImage);
  252. } else {
  253. CGImageRef effectCGImage;
  254. UIImage *effectImage;
  255. if (input != &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
  256. UIGraphicsEndImageContext();
  257. if (input == &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
  258. UIGraphicsEndImageContext();
  259. effectCGImage = effectImage.CGImage;
  260. outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
  261. }
  262. return outputImage;
  263. }
  264. // Helper function to handle deferred cleanup of a buffer.
  265. static void _yy_cleanupBuffer(void *userData, void *buf_data) {
  266. free(buf_data);
  267. }
  268. // Helper function to add tint and mask.
  269. - (UIImage *)_yy_mergeImageRef:(CGImageRef)effectCGImage
  270. tintColor:(UIColor *)tintColor
  271. tintBlendMode:(CGBlendMode)tintBlendMode
  272. maskImage:(UIImage *)maskImage
  273. opaque:(BOOL)opaque {
  274. BOOL hasTint = tintColor != nil && CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__;
  275. BOOL hasMask = maskImage != nil;
  276. CGSize size = self.size;
  277. CGRect rect = { CGPointZero, size };
  278. CGFloat scale = self.scale;
  279. if (!hasTint && !hasMask) {
  280. return [UIImage imageWithCGImage:effectCGImage];
  281. }
  282. UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
  283. CGContextRef context = UIGraphicsGetCurrentContext();
  284. CGContextScaleCTM(context, 1.0, -1.0);
  285. CGContextTranslateCTM(context, 0, -size.height);
  286. if (hasMask) {
  287. CGContextDrawImage(context, rect, self.CGImage);
  288. CGContextSaveGState(context);
  289. CGContextClipToMask(context, rect, maskImage.CGImage);
  290. }
  291. CGContextDrawImage(context, rect, effectCGImage);
  292. if (hasTint) {
  293. CGContextSaveGState(context);
  294. CGContextSetBlendMode(context, tintBlendMode);
  295. CGContextSetFillColorWithColor(context, tintColor.CGColor);
  296. CGContextFillRect(context, rect);
  297. CGContextRestoreGState(context);
  298. }
  299. if (hasMask) {
  300. CGContextRestoreGState(context);
  301. }
  302. UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
  303. UIGraphicsEndImageContext();
  304. return outputImage;
  305. }
  306. - (UIImage *)fixOrientation
  307. {
  308. // No-op if the orientation is already correct.
  309. if (self.imageOrientation == UIImageOrientationUp) {
  310. return self;
  311. }
  312. // We need to calculate the proper transformation to make the image upright.
  313. // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
  314. CGAffineTransform transform = CGAffineTransformIdentity;
  315. switch (self.imageOrientation) {
  316. case UIImageOrientationDown:
  317. case UIImageOrientationDownMirrored:
  318. transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
  319. transform = CGAffineTransformRotate(transform, M_PI);
  320. break;
  321. case UIImageOrientationLeft:
  322. case UIImageOrientationLeftMirrored:
  323. transform = CGAffineTransformTranslate(transform, self.size.width, 0);
  324. transform = CGAffineTransformRotate(transform, M_PI_2);
  325. break;
  326. case UIImageOrientationRight:
  327. case UIImageOrientationRightMirrored:
  328. transform = CGAffineTransformTranslate(transform, 0, self.size.height);
  329. transform = CGAffineTransformRotate(transform, -M_PI_2);
  330. break;
  331. case UIImageOrientationUp:
  332. case UIImageOrientationUpMirrored:
  333. break;
  334. }
  335. switch (self.imageOrientation) {
  336. case UIImageOrientationUpMirrored:
  337. case UIImageOrientationDownMirrored:
  338. transform = CGAffineTransformTranslate(transform, self.size.width, 0);
  339. transform = CGAffineTransformScale(transform, -1, 1);
  340. break;
  341. case UIImageOrientationLeftMirrored:
  342. case UIImageOrientationRightMirrored:
  343. transform = CGAffineTransformTranslate(transform, self.size.height, 0);
  344. transform = CGAffineTransformScale(transform, -1, 1);
  345. break;
  346. case UIImageOrientationUp:
  347. case UIImageOrientationDown:
  348. case UIImageOrientationLeft:
  349. case UIImageOrientationRight:
  350. break;
  351. }
  352. // Now we draw the underlying CGImage into a new context, applying the transform
  353. // calculated above.
  354. CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
  355. CGImageGetBitsPerComponent(self.CGImage), 0,
  356. CGImageGetColorSpace(self.CGImage),
  357. CGImageGetBitmapInfo(self.CGImage));
  358. CGContextConcatCTM(ctx, transform);
  359. switch (self.imageOrientation) {
  360. case UIImageOrientationLeft:
  361. case UIImageOrientationLeftMirrored:
  362. case UIImageOrientationRight:
  363. case UIImageOrientationRightMirrored:
  364. CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.height, self.size.width), self.CGImage);
  365. break;
  366. default:
  367. CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
  368. break;
  369. }
  370. // And now we just create a new UIImage from the drawing context.
  371. CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
  372. UIImage *img = [UIImage imageWithCGImage:cgimg];
  373. CGContextRelease(ctx);
  374. CGImageRelease(cgimg);
  375. return img;
  376. }
  377. + (UIImage *)originImageWithName: (NSString *)name {
  378. return [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
  379. }
  380. - (UIImage *)circleImage {
  381. CGSize size = self.size;
  382. CGFloat drawWH = size.width < size.height ? size.width : size.height;
  383. // 1. 开启图形上下文
  384. UIGraphicsBeginImageContext(CGSizeMake(drawWH, drawWH));
  385. // 2. 绘制一个圆形区域, 进行裁剪
  386. CGContextRef context = UIGraphicsGetCurrentContext();
  387. CGRect clipRect = CGRectMake(0, 0, drawWH, drawWH);
  388. CGContextAddEllipseInRect(context, clipRect);
  389. CGContextClip(context);
  390. // 3. 绘制大图片
  391. CGRect drawRect = CGRectMake(0, 0, size.width, size.height);
  392. [self drawInRect:drawRect];
  393. // 4. 取出结果图片
  394. UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
  395. // 5. 关闭上下文
  396. UIGraphicsEndImageContext();
  397. return resultImage;
  398. }
  399. @end