UIImage+ImageEffect.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // UIImage+ImageEffect.m
  3. // CommonLibrary
  4. //
  5. // Created by AlexiChen on 15/12/24.
  6. // Copyright © 2015年 AlexiChen. All rights reserved.
  7. //
  8. #import "UIImage+ImageEffect.h"
  9. #import <Accelerate/Accelerate.h>
  10. @implementation UIImage (ImageEffect)
  11. - (UIImage *)applyLightEffect
  12. {
  13. UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
  14. return [self blurWithRadius:30 tintColor:tintColor deltaFactor:1.8 maskImage:nil];
  15. }
  16. - (UIImage *)applyExtraLightEffect
  17. {
  18. UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
  19. return [self blurWithRadius:20 tintColor:tintColor deltaFactor:1.8 maskImage:nil];
  20. }
  21. - (UIImage *)applyDarkEffect
  22. {
  23. UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
  24. return [self blurWithRadius:20 tintColor:tintColor deltaFactor:1.8 maskImage:nil];
  25. }
  26. - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
  27. {
  28. const CGFloat EffectColorAlpha = 0.6;
  29. UIColor *effectColor = tintColor;
  30. size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
  31. if (componentCount == 2)
  32. {
  33. CGFloat b;
  34. if ([tintColor getWhite:&b alpha:NULL])
  35. {
  36. effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
  37. }
  38. }
  39. else
  40. {
  41. CGFloat r, g, b;
  42. if ([tintColor getRed:&r green:&g blue:&b alpha:NULL])
  43. {
  44. effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
  45. }
  46. }
  47. return [self blurWithRadius:10 tintColor:effectColor deltaFactor:-1.0 maskImage:nil];
  48. }
  49. - (UIImage *)blurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor deltaFactor:(CGFloat)deltaFactor maskImage:(UIImage *)maskImage
  50. {
  51. // Check pre-conditions.
  52. if (self.size.width < 1 || self.size.height < 1)
  53. {
  54. NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
  55. return nil;
  56. }
  57. if (!self.CGImage)
  58. {
  59. NSLog (@"*** error: image must be backed by a CGImage: %@", self);
  60. return nil;
  61. }
  62. if (maskImage && !maskImage.CGImage)
  63. {
  64. NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
  65. return nil;
  66. }
  67. CGRect imageRect = { CGPointZero, self.size };
  68. UIImage *effectImage = self;
  69. BOOL hasBlur = blurRadius > __FLT_EPSILON__;
  70. BOOL hasSaturationChange = fabs(deltaFactor - 1.) > __FLT_EPSILON__;
  71. if (hasBlur || hasSaturationChange)
  72. {
  73. UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
  74. CGContextRef effectInContext = UIGraphicsGetCurrentContext();
  75. CGContextScaleCTM(effectInContext, 1.0, -1.0);
  76. CGContextTranslateCTM(effectInContext, 0, -self.size.height);
  77. CGContextDrawImage(effectInContext, imageRect, self.CGImage);
  78. vImage_Buffer effectInBuffer;
  79. effectInBuffer.data = CGBitmapContextGetData(effectInContext);
  80. effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
  81. effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
  82. effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
  83. UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
  84. CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
  85. vImage_Buffer effectOutBuffer;
  86. effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
  87. effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
  88. effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
  89. effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
  90. if (hasBlur)
  91. {
  92. // A description of how to compute the box kernel width from the Gaussian
  93. // radius (aka standard deviation) appears in the SVG spec:
  94. // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
  95. //
  96. // For larger values of 's' (s >= 2.0), an approximation can be used: Three
  97. // successive box-blurs build a piece-wise quadratic convolution kernel, which
  98. // approximates the Gaussian kernel to within roughly 3%.
  99. //
  100. // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
  101. //
  102. // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
  103. //
  104. CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
  105. NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
  106. if (radius % 2 != 1)
  107. {
  108. radius += 1; // force radius to be odd so that the three box-blur methodology works.
  109. }
  110. vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (uint32_t)radius, (uint32_t)radius, 0, kvImageEdgeExtend);
  111. vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, (uint32_t)radius, (uint32_t)radius, 0, kvImageEdgeExtend);
  112. vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (uint32_t)radius, (uint32_t)radius, 0, kvImageEdgeExtend);
  113. }
  114. BOOL effectImageBuffersAreSwapped = NO;
  115. if (hasSaturationChange)
  116. {
  117. CGFloat s = deltaFactor;
  118. CGFloat floatingPointSaturationMatrix[] =
  119. {
  120. 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
  121. 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
  122. 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
  123. 0, 0, 0, 1,
  124. };
  125. const int32_t divisor = 256;
  126. NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
  127. int16_t saturationMatrix[matrixSize];
  128. for (NSUInteger i = 0; i < matrixSize; ++i) {
  129. saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
  130. }
  131. if (hasBlur)
  132. {
  133. vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
  134. effectImageBuffersAreSwapped = YES;
  135. }
  136. else
  137. {
  138. vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
  139. }
  140. }
  141. if (!effectImageBuffersAreSwapped)
  142. effectImage = UIGraphicsGetImageFromCurrentImageContext();
  143. UIGraphicsEndImageContext();
  144. if (effectImageBuffersAreSwapped)
  145. effectImage = UIGraphicsGetImageFromCurrentImageContext();
  146. UIGraphicsEndImageContext();
  147. }
  148. // Set up output context.
  149. UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
  150. CGContextRef outputContext = UIGraphicsGetCurrentContext();
  151. CGContextScaleCTM(outputContext, 1.0, -1.0);
  152. CGContextTranslateCTM(outputContext, 0, -self.size.height);
  153. // Draw base image.
  154. CGContextDrawImage(outputContext, imageRect, self.CGImage);
  155. // Draw effect image.
  156. if (hasBlur)
  157. {
  158. CGContextSaveGState(outputContext);
  159. if (maskImage)
  160. {
  161. CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
  162. }
  163. CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
  164. CGContextRestoreGState(outputContext);
  165. }
  166. // Add in color tint.
  167. if (tintColor)
  168. {
  169. CGContextSaveGState(outputContext);
  170. CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
  171. CGContextFillRect(outputContext, imageRect);
  172. CGContextRestoreGState(outputContext);
  173. }
  174. // Output image is ready.
  175. UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
  176. UIGraphicsEndImageContext();
  177. return outputImage;
  178. }
  179. + (void)frostedGlassEffectWithView:(UIView *)view effect:(UIBlurEffect *)effect
  180. {
  181. UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
  182. effectView.frame = view.bounds;
  183. [view addSubview:effectView];
  184. }
  185. @end