Skip to content

Instantly share code, notes, and snippets.

@IgorMuzyka
Last active December 19, 2017 18:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IgorMuzyka/99c1059bcfa3e2bd471d to your computer and use it in GitHub Desktop.
Save IgorMuzyka/99c1059bcfa3e2bd471d to your computer and use it in GitHub Desktop.
Objective-C UIImage Glitch
//
// 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
//
// 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
//
// 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
//
// 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