Last active
August 29, 2015 13:58
-
-
Save coolio107/10164592 to your computer and use it in GitHub Desktop.
CoreImage snippet for UIImage blurring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CoreImage filter chain to blur an arbitrary UIImage | |
// Input: blur radius and target size | |
// This is assumed to be called on a background queue, it takes a response block to process the completed, blurred image | |
// <C> 2013/2014 Joerg Schwieder, use at will; I'd love to see an attribution, of course but don't demand it. | |
// Contains some pseudo code, see comments. | |
// | |
+ (CGFloat)blurRadius { | |
static CGFloat _blurRadius = 20.0; | |
static dispatch_once_t onceToken; | |
// I use different blur radius on iPad and iPhone | |
dispatch_once(&onceToken, ^{ | |
if (IS_ON_PAD) | |
_blurRadius *= 2.0; | |
}); | |
return _blurRadius; | |
} | |
+ (CGFloat)filteredSize { | |
static CGFloat _filteredSize = 320.0; | |
static dispatch_once_t onceToken; | |
// I use different image sizes on iPad and iPhone. | |
// This can, of course, be variable, too, it's being set dynamically on the filter for each run but that filter change takes some time | |
// I also use non-retina resolution because I felt that the lower resolution goes unnoticed (for the blurred image) on retina screens and it cuts the processing by 75% | |
dispatch_once(&onceToken, ^{ | |
if (IS_ON_PAD) | |
_filteredSize = 768.0; | |
}); | |
return _filteredSize; | |
} | |
+ (void)createBlurredImage:(UIImage *)art | |
withTintColor:(UIColor *)blendColor | |
desaturationFactor:(CGFloat)desaturationFactor | |
completion:(void(^)(UIImage * source, UIImage * result))completionHandler; | |
{ | |
static CIFilter * ciBlurFilter; | |
static CIFilter * ciColorControlFilter; | |
static CIContext * ciContext; | |
static CIFilter * ciColorGenerator; | |
static CIFilter * ciCompositor; | |
static dispatch_queue_t filterQueue; | |
// this initializatuon code needs to be run on the main thread. | |
// I use a macro to determine which thread I'm on (I use specific queues I can identify at runtime). | |
// If you don't have that you could add the whole code to a custom class and simply initialize it before use | |
dispatch_sync_safe(dispatch_get_main_queue(), ^{ | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
ciContext = [CIContext contextWithOptions:nil]; | |
ciBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"]; | |
ciColorControlFilter = [CIFilter filterWithName:@"CIColorControls"]; | |
//[ciColorControlFilter setValue:@(desaturationFactor) forKey:@"inputSaturation"]; | |
ciColorGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"]; | |
ciCompositor = [CIFilter filterWithName:@"CISourceOverCompositing"]; | |
filterQueue = dispatch_queue_create("ickBlurFilterQueue", NULL); | |
}); | |
}); | |
if (!art) | |
return; | |
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { | |
completionHandler(art, nil); | |
return; | |
} | |
dispatch_async(filterQueue, ^{ | |
CIImage * ciImage = [CIImage imageWithCGImage:art.CGImage]; | |
CIImage * result; | |
CGFloat scale = (art.size.width / [self filteredSize]); | |
CGFloat blurRadius = [self blurRadius]; | |
CGRect rect = CGRectMake(-(2.0 * blurRadius) * scale, | |
-(2.0 * blurRadius) * scale, | |
scale * (4.0 * blurRadius) + art.size.width, | |
scale * (4.0 * blurRadius) + art.size.height); | |
UIColor * bgClear = [blendColor colorWithAlphaComponent:1.0]; | |
CIColor * ciColor = [CIColor colorWithCGColor:bgClear.CGColor]; | |
if (!ciImage || !bgClear || !ciColor) { | |
completionHandler(art, nil); | |
return; | |
} | |
[ciColorGenerator setValue:ciColor forKey:@"inputColor"]; | |
[ciColorControlFilter setValue:@(desaturationFactor) forKey:@"inputSaturation"]; | |
[ciColorControlFilter setValue:ciImage forKey:kCIInputImageKey]; | |
blurRadius = blurRadius * scale; | |
[ciBlurFilter setValue:@(blurRadius) forKey:@"inputRadius"]; | |
//NSLog(@"Image Size: %f %f, Blur Radius: %f", _coverImage.size.width, _coverImage.size.height, blurRadius); | |
// the compositor filter combines the generated color background and the desaturated image | |
[ciCompositor setValue:ciColorGenerator.outputImage forKey:@"inputBackgroundImage"]; | |
[ciCompositor setValue:ciColorControlFilter.outputImage forKey:@"inputImage"]; | |
// the blur filter takes the compositor output as input | |
[ciBlurFilter setValue:ciCompositor.outputImage forKey:kCIInputImageKey]; | |
result = ciBlurFilter.outputImage; | |
if (!result) { | |
completionHandler(art, nil); | |
return; | |
} | |
CGImageRef cgImage = [ciContext createCGImage:result fromRect:rect]; | |
UIImage * modImage = [UIImage imageWithCGImage:cgImage]; | |
completionHandler(art, modImage); | |
CGImageRelease(cgImage); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment