GPUImage animated gaussian blur filter - objective-c

I was using the GPUImage gaussian blur filter to blur a still image. I want to tie the blur size to an UI element and as the element is changed by the user I blur the picture. The way I am doing it right now is to change the blurSize when there has been a significant (> 0.25) change, reapply the filter and animate the new image into the imageView.
Is there a more efficient way for me to be doing this?
On the iPhone 5, while performance is not laggy, it is not super smooth either (but perhaps that is because this operation is simply too expensive to be super smooth).

interesting question. Your best bet is to pre-compute and render CGImages at a couple of blur scales in your range, then use two stacked UIImageViews, with the one on top showing the blurrier copy but partially transparent. For example, if you rendered blurred at radiuses 1,2,3,…,10, and you wanted to show 3.7, you'd show image 3 on the bottom and image 4 on the top, at 70% opacity.
// Created in answer to Nikhil Varma's question at http://stackoverflow.com/questions/18804668/gpuimage-animated-gaussian-blur-filter
#import "GPUImage.h"
#define BLUR_STEP 2.
#interface AHViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *imageView2;
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (nonatomic) CGFloat blurRadius;
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, strong) GPUImageGaussianBlurFilter *blurFilter;
#property (nonatomic, strong) NSCache *blurredImageCache;
#property (nonatomic, strong) dispatch_queue_t blurQueue;
#property (nonatomic, strong) NSMutableSet *blurAmountsBeingRendered;
#end
#implementation AHViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.image = [UIImage imageNamed:#"ph.jpg"];
self.blurFilter = [GPUImageGaussianBlurFilter new];
self.blurredImageCache = [NSCache new];
self.blurQueue = dispatch_queue_create("Image Blur Queue", DISPATCH_QUEUE_SERIAL);
self.blurAmountsBeingRendered = [NSMutableSet set];
self.blurRadius = 1.0;
}
- (IBAction)sliderDidMove:(UISlider *)sender {
self.blurRadius = 10 * sender.value;
}
-(void)setBlurRadius:(CGFloat)blurRadius {
_blurRadius = blurRadius;
CGFloat smaller = self.blurRadius - fmodf(self.blurRadius, BLUR_STEP);
[self asyncGenerateImageWithBlurAmount:smaller];
CGFloat larger = smaller + BLUR_STEP;
[self asyncGenerateImageWithBlurAmount:larger];
}
-(UIImage *)cachedImageWithBlurAmount:(CGFloat)blur {
return [self.blurredImageCache objectForKey:#(blur)];
}
-(void)asyncGenerateImageWithBlurAmount:(CGFloat)blur {
// This image is already available.
if([self.blurredImageCache objectForKey:#(blur)]) {
[self imageIsAvailableWithBlur:blur];
return;
}
// There's already a render going on for this. Just return.
if([self.blurAmountsBeingRendered containsObject:#(blur)])
return;
// Start a render
[self.blurAmountsBeingRendered addObject:#(blur)];
dispatch_async(self.blurQueue, ^{
self.blurFilter.blurSize = blur;
UIImage *result = [self.blurFilter imageByFilteringImage:self.image];
[self.blurredImageCache setObject:result forKey:#(blur)];
[self.blurAmountsBeingRendered removeObject:#(blur)];
dispatch_async(dispatch_get_main_queue(), ^{
[self imageIsAvailableWithBlur:blur];
});
});
}
-(void)imageIsAvailableWithBlur:(CGFloat)blurAmount {
CGFloat smaller = self.blurRadius - fmodf(self.blurRadius, BLUR_STEP);
CGFloat larger = smaller + BLUR_STEP;
UIImage *sharperImage = [self cachedImageWithBlurAmount:smaller];
UIImage *blurrier = [self cachedImageWithBlurAmount:larger];
if(sharperImage && blurrier) {
if(![self.imageView.image isEqual:sharperImage])
self.imageView.image = sharperImage;
if(![self.imageView2.image isEqual:blurrier]) {
self.imageView2.image = blurrier;
}
self.imageView2.alpha = (self.blurRadius - smaller) / BLUR_STEP;
}
}
#end

Related

Can I update a label in a UIButton without redrawing the button?

I want a clock readout to appear as a label in a UIButton once every second. But even though I remove it from the superView the new UIButton overwrites the old one, like this
… and after several minutes of this my iPhone looked seriously burnt :-)
In PlayViewController my NSTimer methods look like this
- (void)startClock {
clockCount = 0; // start on 1st clock pulse
totalMinutes = 0; // appears on clockButton
totalSeconds = 0; // appears on clockButton
timer = [NSTimer scheduledTimerWithTimeInterval:STATES_ConcertClock
target:self
selector:#selector(nextClock)
userInfo:nil
repeats:YES];
}
- (void)nextClock {
self.lastEventChangeTime = [NSDate date];
clockCount++;
[self masterClockReadout];
}
and here is my clock readout method
- (void)masterClockReadout {
totalMinutes = clockCount / 60;
totalSeconds = clockCount % 60;
clockString = [NSString stringWithFormat:#"%02d:%02d", totalMinutes, totalSeconds];
[self.seconds removeFromSuperview];
EscButton *seconds = [[EscButton alloc] loadEscButton:(NSString *)clockString];
[self.view addSubview:seconds];
}
I also set a UIView property so removeFromSuperview knows what to remove.
#property (nonatomic, retain) UIView* seconds;
My question is: can I update the UIButton label without redrawing the button ? and, is this a problem that might be solved by using a delegate ?
To date my experience using delegates has been for sending messages from the UIButton to ViewController(e.g. below) but so far I haven't found an example I am able to apply where messages are sent in the opposite direction. So if using a delegate is the recommended approach, could you please point me to some code that might help me solve this problem. Thanks
EscButton.h
#import <UIKit/UIKit.h>
#protocol EscButtonDelegate <NSObject>
-(void)fromEscButton:(UIButton*)button;
#end
#interface EscButton : UIView {
}
- (id)loadEscButton:(NSString *)text;
#property (assign) id<EscButtonDelegate> delegate;
#end
EscButton.m
#import "EscButton.h"
#implementation EscButton
- (id)loadEscButton:(NSString *)text {
CGFloat sideOffset = screenWidth - ESC_BUTTON_Width - MARGIN_Side;
CGFloat topOffset = statusBarHeight + MARGIN_Top;
UIButton *escButton = [UIButton buttonWithType:UIButtonTypeCustom];
escButton.frame = CGRectMake(sideOffset, topOffset, ESC_BUTTON_Width, ESC_BUTTON_Height);
// etc …
[escButton addTarget:self.delegate action:#selector(fromEscButton:) forControlEvents:UIControlEventTouchUpInside];
[escButton setTitle:text forState:UIControlStateNormal];
// etc …
return escButton;
}
#end
There is no need to constantly add and remove a button. You can keep the same one. Change the following code:
#property (nonatomic, retain) UIView* seconds;
to:
#property (nonatomic, retain) UIButton *secondsB;
Also change:
[self.seconds removeFromSuperview];
EscButton *seconds = [[EscButton alloc] loadEscButton:(NSString *)clockString];
[self.view addSubview:seconds];
to:
if (!self.secondsB) {
self.secondsB = [[EscButton alloc] loadEscButton:(NSString *)clockString];
[self.view addSubview:_secondsB]; // previously addSubview:seconds];
}
[self.secondsB setTitle:clockString forState:UIControlStateNormal];

Array of UIButtons, can't change button image

I have an array of UIButton objects (I have 15 buttons so I wanted to store them in an array for easy processing). I want to be able to iterate through the array and change the background image/image that the button uses. This is how I currently have it set up:
#property (nonatomic, weak) UIImage *greenLight;
#property (nonatomic, weak) UIImage *yellowLight;
#property (nonatomic, weak) UIImage *redLight;
#property (weak, nonatomic) IBOutlet UIButton *task1Button;
#property (weak, nonatomic) IBOutlet UIButton *task2Button;
#property (weak, nonatomic) IBOutlet UIButton *task3Button;
#property (nonatomic, copy) NSMutableArray *buttonArray;
- (instancetype)init
{
self = [super init];
if(self){
[self.buttonArray addObject:self.task1Button];
[self.buttonArray addObject:self.task2Button];
[self.buttonArray addObject:self.task3Button];
self.greenLight = [UIImage imageNamed:#"greenlight.ICO"];
self.yellowLight = [UIImage imageNamed:#"yellowlight.ICO"];
self.redLight = [UIImage imageNamed:#"redlight.ICO"];
NSLog(#"init complete...");
}
return self;
}
Then I have the following in viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.task1Button setImage:self.redLight forState:UIControlStateNormal];
[(UIButton *)self.buttonArray[1] setImage:self.greenLight forState:UIControlStateNormal];
}
The second line in viewWillAppear works and changes the button's image. The third line, however, does not. The debugger shows the code reaching that line and not throwing any errors, so I don't know why setting the button image when the button is in an array would not work.
Any ideas?
Try replacing last line in your answer with this :
[(UIButton *)[self.buttonArray objectAtIndex:1] setImage:self.greenLight forState:UIControlStateNormal];
But i'm doing this without compiler so i'm not really sure about where to place the first bracket [
Tell me if it works, i'm not totally sure about it.

How can I rotate a UIImageView?

i'm pretty new to Xcode, and i was wondering how to rotate a uiimage on touch down, this is what i have currently:
#implementation secondViewController
int degrees;
UIImageView *gliderImageView;
bool continueSpinning;
-(void)startSpinning {
degrees = 0;
continueSpinning = true;
[self continueSpinning];
}
-(void)continueSpinning {
degrees = (degrees + 1) %360;
CGAffineTransform rotate = CGAffineTransformMakeRotation( degrees / 180.0 * 3.14);
[gliderImageView setTransform:rotate];
if(!continueSpinning) return;
else [self performSelector:#selector(continueSpinning) withObject:nil afterDelay:0.1f];
}
-(void)stopSpinning {
continueSpinning = false;
}
so does anyone know
how to link the button to the image
how to set a boolean with a button
if this code will work in the first place?
You can find an example of rotating a UIImage here.
So just in your .h write this:
(in the #interface)
IBOutlet UIImage *myImage;
IBOutlet UIButton *myButton;
(after the #interface closing bracket)
#property (nonatomic, retain) IBOutlet UIImage *myImage;
#property (nonatomic, retain) IBOutlet UIButton *myButton
-(IBAction) spinIt:(id)sender;
Then in your .m write:
Under the #implementation:
#synthesize myImage, myButton;
Then below the end of the viewDidLoad write something like this:
-(IBAction)spinIt:(id)sender {
myImage.image = image;
[self addSubview:imageView];
CGAffineTransform rotate = CGAffineTransformMakeRotation( 1.0 / 20.0 * M_PI );
[imageView setTransform:rotate];
}
The above code is from here.
After that add an image and a button to your view in the Storyboard and set up the references by control dragging from the button to the responder and clicking the spinIt method and by control dragging from the outlet names on the far right in the connections tab under the little arrow icon to their respective connections.
The above code rotates 90 degrees so you may want to change it to fit your needs. Also, this is a duplicate question, so in the future please search before posting. Good luck!
EDIT- If you want the image to spin while the button is being pressed I recommend making an array of images of it at different intervals in the spin and playing the images like a stop motion animation. Instructions on that can be found [here]3.

Objective-c floating button, same procedure different behaviour

I have a created a floating button on a UITableViewController. It works like expected.
Now I wanted to use the button as well on a UIViewController where I added a UITableView.
The code for creating the floating button is identically, but the behaviour is different.
At the UITableViewController the button stay where it shall.
At the UIViewController the button moves up (when I scroll down) and down (when I scroll up)
How do I get the button stay where it shall on the UIViewController. Like on the UITableViewController?
TableViewController.h
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "SingletonClass.h"
#interface MagTableViewController : UITableViewController <UIGestureRecognizerDelegate>
#property (strong, nonatomic) IBOutlet UITableView *magTableView;
#property (strong, nonatomic) UIButton *oButton;
#property (nonatomic) float originalOrigin;
#end
TableViewController.m - floating button, works like expected
- (void)viewDidLoad
{
[super viewDidLoad];
SingletonClass *sharedSingleton = [SingletonClass sharedInstance];
self.originalOrigin = sharedSingleton.photoButtonY; // 430.0
[self addOverlayButton];
}
#pragma mark Camera Button
- (void)addOverlayButton {
self.oButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.oButton.frame = CGRectMake(132.0, self.originalOrigin, 56.0, 56.0);
[self.oButton setImage:[UIImage imageNamed:#"CameraButton56x56.png"] forState:UIControlStateNormal];
[self.oButton addTarget:self action:#selector(toCameraView:) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:self.oButton aboveSubview:self.view];
}
// to make the button float over the tableView including tableHeader
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect tableBounds = self.tableView.bounds; // should be _magTableView, but works with _tableView as well. But I do not know where this _tableView comes from...
//CGRect tableBounds = self.magTableView.bounds;
// floating Button;
CGRect floatingButtonFrame = self.oButton.frame;
floatingButtonFrame.origin.y = self.originalOrigin + tableBounds.origin.y;
self.oButton.frame = floatingButtonFrame;
[self.view bringSubviewToFront:self.oButton]; // float over the tableHeader
}
ViewController.h
#import <UIKit/UIKit.h>
#import "SingletonClass.h"
#interface DetailViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) UIButton *oButton;
#property (nonatomic) float originalOrigin;
#end
ViewController.m - Jumping Button, not wanted behaviour (Update: works no, no jumping anymore)
- (void)viewDidLoad
{
[super viewDidLoad];
self.originalOrigin = [[SingletonClass sharedInstance] photoButtonY]; // 430.0
[self addOverlayButton];
[self initTableView]; // initiate the tableview and works properly
}
#pragma mark Camera Button
- (void)addOverlayButton {
self.oButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.oButton.frame = CGRectMake(132.0, self.originalOrigin, 56.0, 56.0);
[self.oButton setImage:[UIImage imageNamed:#"CameraButton56x56.png"] forState:UIControlStateNormal];
[self.oButton addTarget:self action:#selector(toCameraView:) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:self.oButton aboveSubview:self.view];
}
/** Remove this function and the button stayed where I expected it
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect tableBounds = self.tableView.bounds;
// floating Button;
CGRect floatingButtonFrame = self.oButton.frame;
floatingButtonFrame.origin.y = self.originalOrigin + tableBounds.origin.y;
self.oButton.frame = floatingButtonFrame;
[self.view bringSubviewToFront:self.oButton]; // float over the tableHeader
}
*/

Disable accelerometer for a period of time then enable it

I have an app where u can move a man with the accelerometer. There is a ready set go sequence and then using pop Animation the guy appears. Although even when the man is not visible and the ready set go words appear the accelerometer is still usable and u can move the guy and accidentally touch the obstacle which then using cgrectintersectsrect changes to An end game screen. So in that ready set go sequence how can I disable the accelerometer then reenable it when the go word appears?
.h:
#interface GameScreen : UIViewController <UIAccelerometerDelegate> {
IBOutlet UIImageView *image1;
IBOutlet UIImageView *image2;
UIImageView *ball;
CGPoint delta;
IBOutlet UIImageView *man;
IBOutlet UIImageView *fang;
IBOutlet UIImageView* hiddenView;
IBOutlet UILabel* ready;
IBOutlet UILabel* set;
IBOutlet UILabel* go;
IBOutlet UILabel* endScreen;
IBOutlet UIImageView* guy;
CAKeyframeAnimation *popAnimation;
}
#property (nonatomic, retain)UIImageView *image1;
#property (nonatomic, retain)UIImageView *image2;
-(void)checkCollision;
#property (nonatomic, retain)IBOutlet UIImageView *ball;
#property CGPoint delta;
#end
.m:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
NSLog(#"x : %g", acceleration.x);
NSLog(#"y : %g", acceleration.y);
NSLog(#"z : %g", acceleration.z);
delta.y = acceleration.y * 50;
delta.x = acceleration.x * 50;
ball.center = CGPointMake(ball.center.x + delta.x, ball.center.y + delta.y);
// Right
if(ball.center.x < 0) {
ball.center = CGPointMake(320, ball.center.y);
}
// Left
if(ball.center.x > 320) {
ball.center = CGPointMake(0, ball.center.y);
}
// Top
if(ball.center.y < 0) {
ball.center = CGPointMake(ball.center.x, 460);
}
// Bottom
if(ball.center.y > 460){
ball.center = CGPointMake(ball.center.x, 0);
}
[self checkCollision];
}
You really should be reading a good book on coding and doing some tutorials to get an idea of how to do this, but the concept of a flag is simple.
In the class where your man is moved have a bool called bCanMove or similar, and set that to false when the class is initialised.
Once your "ready, steady, go" sequence finishes you set that to true.
The next step is check this flag in the code where you perform your movement, obviously I can't see your code but I expect you've got a method with a name similar to didAccelerate — in there just check to see if the boolean is true or not, and don't do anything if it isn't, for example:
if(!bCanMove)
{
return;
}
As I can't see your code I don't know whether you're relying on inbuilt mechanisms etc., but as other people have stated (and myself) you shouldn't really be asking these kinds of questions on here — not because you're new to coding but more the way you've phrased the question, nobody can really help without any details and it's something you would't need to ask if you did a little more reading ;)
At a bare minimum you should read this: http://cocoadevcentral.com/d/learn_objectivec/ but that's not going to help you if you don't understand some basic programming concepts (and it would appear you don't) — for that there is no substitute for a good book/tutorial.