Last active
December 19, 2017 18:17
-
-
Save IgorMuzyka/99c1059bcfa3e2bd471d to your computer and use it in GitHub Desktop.
Objective-C UIImage Glitch
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
// | |
// GLGLitchEffect.h | |
// Glitch | |
// | |
// Created by Igor on 27.08.14. | |
// Copyright (c) 2014 Igor Muzyka. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface GLGlitchEffect : NSObject | |
// port of https://github.com/snorpey/glitch-canvas/blob/master/src/glitch-canvas.js | |
- (instancetype)initWithImage:(UIImage *)image andParameters:(NSDictionary *)parameters; | |
- (UIImage *)glitch; | |
- (void)updateParameters:(NSDictionary *)parameters; | |
- (void)setImage:(UIImage *)image; | |
- (void)setSeed:(NSUInteger)seed; | |
- (void)setQuality:(NSUInteger)quality; | |
- (void)setAmount:(NSUInteger)amount; | |
- (void)setIterations:(NSUInteger)iterations; | |
@end |
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
// | |
// GLGLitchEffect.m | |
// Glitch | |
// | |
// Created by Igor on 27.08.14. | |
// Copyright (c) 2014 Igor Muzyka. All rights reserved. | |
// | |
#import "GLGlitchEffect.h" | |
@interface GLGlitchEffect () | |
@property (nonatomic, strong) UIImage *image; | |
@property (nonatomic, assign) NSUInteger seed; | |
@property (nonatomic, assign) NSUInteger quality; | |
@property (nonatomic, assign) NSUInteger amount; | |
@property (nonatomic, assign) NSUInteger iterations; | |
@end | |
@implementation GLGlitchEffect | |
- (id)initWithImage:(UIImage *)image andParameters:(NSDictionary *)parameters { | |
self = [super init]; | |
if (self) { | |
_image = image; | |
[self updateParameters:parameters]; | |
} | |
return self; | |
} | |
- (void)updateParameters:(NSDictionary *)parameters { | |
self.seed = [parameters[@"seed"] integerValue]; | |
self.quality = [parameters[@"quality"] integerValue]; | |
self.amount = [parameters[@"amount"] integerValue]; | |
self.iterations = [parameters[@"iterations"] integerValue]; | |
} | |
- (UIImage *)glitch { | |
CGDataProviderRef provider = CGImageGetDataProvider(self.image.CGImage); | |
NSData *data = (id)CFBridgingRelease(CGDataProviderCopyData(provider)); | |
data = [data base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength]; | |
uint8_t *bytes = (uint8_t *)[data bytes]; | |
NSUInteger length = sizeof(uint8_t) * data.length; | |
NSUInteger jpegHeaderLength = [self jpegHeaderLengthFromByteArray:bytes withLength:length]; | |
for (NSUInteger i = 0; i < self.iterations; ++i) { | |
[self glitchJpegBytesInByteArray:bytes | |
withLength:length | |
jpegHeaderLength:jpegHeaderLength | |
andIteration:i]; | |
} | |
data = [NSData dataWithBytes:bytes length:length]; | |
data = [[NSData alloc] initWithBase64EncodedData:data options:NSDataBase64DecodingIgnoreUnknownCharacters]; | |
bytes = (uint8_t *)[data bytes]; | |
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
CGContextRef context = CGBitmapContextCreate(bytes, self.image.size.width, self.image.size.height, 8, self.image.size.width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast); | |
CGImageRef imageRef = CGBitmapContextCreateImage(context); | |
CGContextRelease(context); | |
CGColorSpaceRelease(colorSpace); | |
UIImage *image = [UIImage imageWithCGImage:imageRef]; | |
CGImageRelease(imageRef); | |
return image; | |
return nil; | |
} | |
- (void)glitchJpegBytesInByteArray:(uint8_t *)byteArray | |
withLength:(NSUInteger)length | |
jpegHeaderLength:(NSUInteger)jpegHeaderLength | |
andIteration:(NSUInteger)iteration { | |
NSUInteger maximalIndex = length - jpegHeaderLength - 4; | |
NSUInteger pxMin = maximalIndex / self.iterations * iteration; | |
NSUInteger pxMax = maximalIndex / self.iterations * (iteration + 1); | |
NSUInteger delta = pxMax - pxMin; | |
NSUInteger pxI = pxMin + delta * self.seed; | |
if (pxI > maximalIndex) { | |
pxI = maximalIndex; | |
} | |
NSUInteger index = jpegHeaderLength + pxI; | |
// byteArray[index] = self.amount * 256; | |
NSUInteger value = arc4random() % 256; | |
// value = 256 % self.amount; | |
// NSUInteger index = arc4random() % (maximalIndex - jpegHeaderLength - 4) + jpegHeaderLength; | |
byteArray[index] = value; | |
} | |
- (NSUInteger)jpegHeaderLengthFromByteArray:(const uint8_t *)byteArray withLength:(NSUInteger)length { | |
NSUInteger jpegHeaderLength = 417; | |
for (NSUInteger i = 0; i < length; ++i) { | |
if (byteArray[i] == 255 && byteArray[i + 1] == 218) { | |
jpegHeaderLength = i + 2; | |
break; | |
} | |
} | |
return jpegHeaderLength; | |
} | |
@end |
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
// | |
// ViewController.h | |
// Glitch | |
// | |
// Created by Igor Muzyka on 8/27/14. | |
// Copyright (c) 2014 none. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@interface GLViewController : UIViewController | |
@end |
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
// | |
// GLViewController.m | |
// Glitch | |
// | |
// Created by Igor on 26.08.14. | |
// Copyright (c) 2014 Igor Muzyka. All rights reserved. | |
// | |
#import "GLViewController.h" | |
#import "GLGlitchEffect.h" | |
@interface GLViewController () | |
@property (nonatomic, strong) GLGlitchEffect *glitch; | |
@property (nonatomic, strong) UIImage *image; | |
@property (nonatomic, strong) UIImageView *imageView; | |
@property (nonatomic, strong) UISlider *slider1; | |
@property (nonatomic, strong) UISlider *slider2; | |
@property (nonatomic, strong) UISlider *slider3; | |
@property (nonatomic, strong) UISlider *slider4; | |
@end | |
@implementation GLViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
self.image = [UIImage imageNamed:@"scott_pilgrim_vs_the_world44.jpg"]; | |
NSDictionary *parameters = @{@"seed": @32, | |
@"quality": @18, | |
@"amount": @36, | |
@"iterations": @53}; | |
self.glitch = [[GLGlitchEffect alloc] initWithImage:self.image andParameters:parameters]; | |
self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; | |
self.imageView.contentMode = UIViewContentModeScaleAspectFit; | |
[self.view addSubview:self.imageView]; | |
self.imageView.image = [self.glitch glitch]; | |
self.slider1 = [[UISlider alloc] initWithFrame:(CGRect){0, 20, 320, 20}]; | |
self.slider2 = [[UISlider alloc] initWithFrame:(CGRect){0, 80, 320, 20}]; | |
self.slider4 = [[UISlider alloc] initWithFrame:(CGRect){0, 200, 320, 20}]; | |
self.slider3 = [[UISlider alloc] initWithFrame:(CGRect){0, 140, 320, 20}]; | |
[self.slider1 setMaximumValue:100]; | |
[self.slider2 setMaximumValue:100]; | |
[self.slider3 setMaximumValue:100]; | |
[self.slider4 setMaximumValue:100]; | |
[self.slider1 setContinuous:NO]; | |
[self.slider2 setContinuous:NO]; | |
[self.slider3 setContinuous:NO]; | |
[self.slider4 setContinuous:NO]; | |
[self.slider1 addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; | |
[self.slider2 addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; | |
[self.slider3 addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; | |
[self.slider4 addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; | |
[self.view addSubview:self.slider1]; | |
[self.view addSubview:self.slider2]; | |
[self.view addSubview:self.slider3]; | |
[self.view addSubview:self.slider4]; | |
} | |
- (void)sliderChanged:(UISlider *)slider { | |
NSDictionary *parameters = @{@"seed": @(self.slider1.value), | |
@"quality": @(self.slider2.value), | |
@"amount": @(self.slider3.value), | |
@"iterations": @(self.slider4.value)}; | |
[self.glitch updateParameters:parameters]; | |
self.imageView.image = [self.glitch glitch]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment