Skip to content

Instantly share code, notes, and snippets.

@elpsk
Created May 15, 2014 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elpsk/eaaed930407ff1cb69b9 to your computer and use it in GitHub Desktop.
Save elpsk/eaaed930407ff1cb69b9 to your computer and use it in GitHub Desktop.
@interface UIImage (Blur)
- (UIImage *)imageWithGaussianBlur;
- (UIImage*) stackBlur:(NSUInteger)radius;
- (UIImage *) normalize;
+ (void) applyStackBlurToBuffer:(UInt8*)targetBuffer width:(const int)w height:(const int)h withRadius:(NSUInteger)inradius;
@end
#import "UIImage+Blur.h"
#define SQUARE(i) ((i)*(i))
inline static void zeroClearInt(int* p, size_t count) { memset(p, 0, sizeof(int) * count); }
@implementation UIImage (Blur)
- (UIImage *)imageWithGaussianBlur
{
float weight[5] = {0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162};
UIGraphicsBeginImageContext(self.size);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[0]];
for (int x = 1; x < 5; ++x) {
[self drawInRect:CGRectMake(x, 0, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[x]];
[self drawInRect:CGRectMake(-x, 0, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[x]];
}
UIImage *horizBlurredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContext(self.size);
[horizBlurredImage drawInRect:CGRectMake(0, 0, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[0]];
for (int y = 1; y < 5; ++y) {
[horizBlurredImage drawInRect:CGRectMake(0, y, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[y]];
[horizBlurredImage drawInRect:CGRectMake(0, -y, self.size.width, self.size.height) blendMode:kCGBlendModePlusLighter alpha:weight[y]];
}
UIImage *blurredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return blurredImage;
}
// +---------------------------------------------------------------------------+
#pragma mark - - - - - - - - - - -
// +---------------------------------------------------------------------------+
- (UIImage*) stackBlur:(NSUInteger)inradius
{
if (inradius < 1){
return self;
}
if (CGSizeEqualToSize(self.size, CGSizeZero)) {
return self;
}
CGImageRef inImage = self.CGImage;
int nbPerCompt = CGImageGetBitsPerPixel(inImage);
if(nbPerCompt != 32){
UIImage *tmpImage = [self normalize];
inImage = tmpImage.CGImage;
}
CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
CFMutableDataRef m_DataRef = CFDataCreateMutableCopy(0, 0, dataRef);
CFRelease(dataRef);
UInt8 * m_PixelBuf=malloc(CFDataGetLength(m_DataRef));
CFDataGetBytes(m_DataRef,
CFRangeMake(0,CFDataGetLength(m_DataRef)) ,
m_PixelBuf);
CGContextRef ctx = CGBitmapContextCreate(m_PixelBuf,
CGImageGetWidth(inImage),
CGImageGetHeight(inImage),
CGImageGetBitsPerComponent(inImage),
CGImageGetBytesPerRow(inImage),
CGImageGetColorSpace(inImage),
CGImageGetBitmapInfo(inImage)
);
const int imageWidth = CGImageGetWidth(inImage);
const int imageHeight = CGImageGetHeight(inImage);
[self.class applyStackBlurToBuffer:m_PixelBuf
width:imageWidth
height:imageHeight
withRadius:inradius];
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
CGContextRelease(ctx);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CFRelease(m_DataRef);
free(m_PixelBuf);
return finalImage;
}
+ (void) applyStackBlurToBuffer:(UInt8*)targetBuffer width:(const int)w height:(const int)h withRadius:(NSUInteger)inradius {
const int radius = inradius;
const int wm = w - 1;
const int hm = h - 1;
const int wh = w*h;
const int div = radius + radius + 1;
const int r1 = radius + 1;
const int divsum = SQUARE((div+1)>>1);
int stack[div*3];
zeroClearInt(stack, div*3);
int vmin[MAX(w,h)];
zeroClearInt(vmin, MAX(w,h));
int *r = malloc(wh*sizeof(int));
int *g = malloc(wh*sizeof(int));
int *b = malloc(wh*sizeof(int));
zeroClearInt(r, wh);
zeroClearInt(g, wh);
zeroClearInt(b, wh);
const size_t dvcount = 256 * divsum;
int *dv = malloc(sizeof(int) * dvcount);
for (int i = 0;(size_t)i < dvcount;i++) {
dv[i] = (i / divsum);
}
int x, y;
int *sir;
int routsum,goutsum,boutsum;
int rinsum,ginsum,binsum;
int rsum, gsum, bsum, p, yp;
int stackpointer;
int stackstart;
int rbs;
int yw = 0, yi = 0;
for (y = 0;y < h;y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for(int i = -radius;i <= radius;i++){
sir = &stack[(i + radius)*3];
int offset = (yi + MIN(wm, MAX(i, 0)))*4;
sir[0] = targetBuffer[offset];
sir[1] = targetBuffer[offset + 1];
sir[2] = targetBuffer[offset + 2];
rbs = r1 - abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0){
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0;x < w;x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = &stack[(stackstart % div)*3];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0){
vmin[x] = MIN(x + radius + 1, wm);
}
int offset = (yw + vmin[x])*4;
sir[0] = targetBuffer[offset];
sir[1] = targetBuffer[offset + 1];
sir[2] = targetBuffer[offset + 2];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = &stack[(stackpointer % div)*3];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0;x < w;x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius*w;
for(int i = -radius;i <= radius;i++) {
yi = MAX(0, yp) + x;
sir = &stack[(i + radius)*3];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - abs(i);
rsum += r[yi]*rbs;
gsum += g[yi]*rbs;
bsum += b[yi]*rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0;y < h;y++) {
int offset = yi*4;
targetBuffer[offset] = dv[rsum];
targetBuffer[offset + 1] = dv[gsum];
targetBuffer[offset + 2] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = &stack[(stackstart % div)*3];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0){
vmin[y] = MIN(y + r1, hm)*w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = &stack[stackpointer*3];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
free(r);
free(g);
free(b);
free(dv);
}
- (UIImage *) normalize {
int width = self.size.width;
int height = self.size.height;
CGColorSpaceRef genericColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef thumbBitmapCtxt = CGBitmapContextCreate(NULL,
width,
height,
8, (4 * width),
genericColorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(genericColorSpace);
CGContextSetInterpolationQuality(thumbBitmapCtxt, kCGInterpolationDefault);
CGRect destRect = CGRectMake(0, 0, width, height);
CGContextDrawImage(thumbBitmapCtxt, destRect, self.CGImage);
CGImageRef tmpThumbImage = CGBitmapContextCreateImage(thumbBitmapCtxt);
CGContextRelease(thumbBitmapCtxt);
UIImage *result = [UIImage imageWithCGImage:tmpThumbImage];
CGImageRelease(tmpThumbImage);
return result;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment