I'm trying to integrate GPUImage into my app. Specifically, I want to apply the Sphere Refraction filter on my main view. Thing is, GPUImage works with UIImage, not with UIView. In order to create a UIImage representation of my view hierarchy, I'm using [CALayer renderInContext], which takes a long time to complete. The net result is that my animations look clunky.
Here's the code that's called in my CADisplayLink handler:
- (void)onDisplayLink:(CADisplayLink*)theDisplayLink {
self.mainView.layer.opaque = YES;
UIGraphicsBeginImageContextWithOptions(self.sphereView.bounds.size, self.sphereView.opaque, [[UIScreen mainScreen] scale]);
[self.sphereView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* mainViewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.sourcePicture = [[GPUImagePicture alloc] initWithImage:mainViewImage smoothlyScaleOutput:NO];
self.sphereRefractionFilter = [[GPUImageSphereRefractionFilter alloc] init];
self.sphereRefractionFilter.radius = 0.5;
self.sphereRefractionFilter.refractiveIndex = 0.25;
[self.sphereRefractionFilter setInputRotation:kGPUImageRotate180 atIndex:0];
[self.sphereRefractionFilter addTarget:self.mainView];
[self.sourcePicture addTarget:self.sphereRefractionFilter];
[self.sourcePicture processImage];
}
The view I'm trying to render using this code has a background image, and about 5-50 smaller images laid out on it, whose positions are modified in real-time. Imagine a sphere with multiple moving markers on it in various places.
Using this code, I'm able to render about 10 FPS. Question is: is there any way to do this faster?
Anyone?
Related
I have a simple application which consists of a UITableViewController with cells of Languages. When the user clicks a specific language, it brings up a UIPageViewController to show horizontally, 6 images in a pageView. That part is working really well and the images are loading.
I have implemented this, using this tutorial: http://code.tutsplus.com/tutorials/using-scrollstyle-with-uipageviewcontroller--mobile-13551.
I would like to achieve a zoom and pan functionality within this as well now.
I have zoom working with the following code.
CGFloat scale = pinchRecognizer.scale;
self.pageViewController.view.transform = CGAffineTransformScale(self.pageViewController.view.transform, scale, scale);
pinchRecognizer.scale = 1.0;
The problem is that the zoom is always in the centre of the image with no way to pan around the image like you can in a normal Photo on an iOS device.
I have found lots of good tutorials on how to achieve this, like http://iosdeveloperzone.com/2012/07/07/tutorial-all-about-images-part-2-panning-zooming-with-uiscrollview/ as an example. However, my confusion stems from the very first part.
I am loading my UIPageViewController using a NSArray:
self.modelArray = [NSMutableArray arrayWithObjects:[[ImageModel alloc] initWithImageName:#"3facts-english-page1.jpg"], [[ImageModel alloc] initWithImageName:#"3facts-english-page2.jpg"], [[ImageModel alloc] initWithImageName:#"3facts-english-page3.jpg"], nil];
How to I extract each image into a UIImageView?
I can see that fundamentally, I need to have most of this code to enable the Pan:
UIImage* image = [UIImage imageNamed:#"KinkakuJi"];
self.imageView.image = image;
[self.imageView sizeToFit];
self.scrollView.contentSize = image.size;
In my case however, I'm not loading a single UIImage but rather a NSArray. How do I extract the UIImage from the NSArray to make this pan happen?
Any thoughts on this would really be appreciated.
I am trying to capture screen portion to post image on social media.
I am using following code to capture screen.
- (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Above code is perfect for capturing screen.
Problem :
My UIView contains GPUImageView with the filtered image. When I tries to capture screen using above code, that particular portion of GPUImageView does not contains the filtered image.
I am using GPUImageSwirlFilter with the static image (no camera). I have also tried
UIImage *outImage = [swirlFilter imageFromCurrentFramebuffer]
but its not giving image.
Note : Following is working code, which gives perfect output of swirl effect, but I want same image in UIImage object.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
GPUImageSwirlFilter *swirlFilter = [GPUImageSwirlFilter alloc] init];
swirlLevel = 4;
[swirlFilter setAngle:(float)swirlLevel/10];
UIImage *inputImage = [UIImage imageNamed:gi.wordImage];
GPUImagePicture *swirlSourcePicture = [[GPUImagePicture alloc] initWithImage:inputImage];
inputImage = nil;
[swirlSourcePicture addTarget:swirlFilter];
dispatch_async(dispatch_get_main_queue(), ^{
[swirlFilter addTarget:imgSwirl];
[swirlSourcePicture processImage];
// This works perfect and I have filtered image in my imgSwirl. But I want
// filtered image in UIImage to use at different place like posting
// on social media
sharingImage = [swirlFilter imageFromCurrentFramebuffer]; // This also
// returns nothing.
});
});
1) Am I doing something wrong with GPUImage's imageFromCurrentFramebuffer ?
2) And why does screen capture code is not including GPUImageView portion in output image ?
3) How do I get filtered image in UIImage ?
First, -renderInContext: won't work with a GPUImageView, because a GPUImageView renders using OpenGL ES. -renderinContext: does not capture from CAEAGLLayers, which are used to back views presenting OpenGL ES content.
Second, you're probably getting a nil image in the latter code because you've forgotten to set -useNextFrameForImageCapture on your filter before triggering -processImage. Without that, your filter won't hang on to its backing framebuffer long enough to capture an image from it. This is due to a recent change in the way that framebuffers are handled in memory (although this change did not seem to get communicated very well).
I wrote this code to change the brightness of an UIImage via an UISlider and the GPUImageBrightnessFilter. But every time I'll test it the app crashes.
My code:
- (IBAction)sliderBrightness:(id)sender {
CGFloat midpoint = [(UISlider *)sender value];
[(GPUImageTiltShiftFilter *)brightnessFilter setTopFocusLevel:midpoint - 0.1];
[(GPUImageTiltShiftFilter *)brightnessFilter setBottomFocusLevel:midpoint + 0.1];
[sourcePicture processImage];
}
- (void) brightnessFilter {
UIImage *inputImage = imgView.image;
sourcePicture = [[GPUImagePicture alloc] initWithImage:inputImage smoothlyScaleOutput:YES];
brightnessFilter = [[GPUImageTiltShiftFilter alloc] init];
// sepiaFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
GPUImageView *imageView = (GPUImageView *)self.view;
[brightnessFilter forceProcessingAtSize:imageView.sizeInPixels]; // This is now needed to make the filter run at the smaller output size
[sourcePicture addTarget:brightnessFilter];
[brightnessFilter addTarget:imageView];
[sourcePicture processImage];
}
Let me make an alternative architectural suggestion. Instead of creating a GPUImagePicture and GPUImageBrightnessFilter each time you change the brightness, then saving that out as a UIImage to a UIImageView, it would be far more efficient to reuse the initial picture and filter and render that to a GPUImageView.
Take a look at what I do in the SimpleImageFilter example that comes with GPUImage. For the tilt-shifted image that's displayed to the screen, I create a GPUImagePicture of the source image once, create one instance of the tilt-shift filter, and then send the output to a GPUImageView. This avoids the expensive (both performance and memory-wise) process of going to a UIImage and then displaying that in a UIImageView, and will be much, much faster. While you're at it, you can use -forceProcessingAtSize: on your filter to only render as many pixels as will be displayed in your final view, also speeding things up.
When you have the right settings for filtering your image, and you want the final UIImage out, you can do one last render pass to extract the processed UIImage. You'd set your forced size back to 0 right before doing that, so you now process the full image.
I would like to quickly animate a blur on a UIView to use as a transition in my app. I'm having trouble knowing where to start. I believe core image is the proper tool for the job. Can anyone point me to a sample of how to blur a UIView? I'm assuming I will need to convert the view into a single UIImage, but I don't know where to proceed from there.
Thanks in advance!
Taking a snapshot of the View and using GPUImage from Brad Larson (the GPUImageGaussianBlurFilter) got me some nice results.
To animate the view I created a ImageView with the blurred image and animated the alpha channel from 0 to 1 to make the blur appear progressively.
Alternatively, I presume its possible to increase the blursize per frame.
#import "GPUImage.h"
...
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
...
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
...
GPUImageGaussianBlurFilter * filter = [[GPUImageGaussianBlurFilter alloc] init];
filter.blurSize = 0.5;
UIImage * blurred = [filter imageByFilteringImage:image];
rasterizeScale of a uiview's layer is what you need, Here is the code for adding blur effect to UIVIew:
CALayer *layer = [self.blurView layer];
[layer setRasterizationScale:0.3];
[layer setShouldRasterize:YES];
For details refer to Apple Documentation of CALayer, Also this tutorial might help You, hope that helps
I recently did some tests with blurring a series of images at different blur settings and animating them simply with UIImageView. You might want to take a look:
AnimatedGaussianBlur
I am trying to implement my own map engine by using CATiledLayer + UIScrollView.
In drawLayer:inContext: method of my implementation, if I have a certain tile image needed for the current bounding box, I immediately draw it in the context.
However, when I do not have one available in the local cache data structure, the tile image is asynchronously requested/downloaded from a tile server, and not draw anything in the context.
The problem is, when I don't draw anything in the context, that part of the view is shown as a blank tile. And the expected behavior is to show the zoom-in scaled tile view from the previous zoom level.
If you guys have faced any similar problem and found any solution for this, please let me know.
You have to setNeedsDisplayInRect: for the tile as soon as you have the data. You have to live with it being blank until the tile is available because you have no way to affect which tiles CATiledLayer is creating.
I do the same, blocking the thread until the tile has been downloaded. The performance is good, it runs smoothly. I'm using a queue to store every tile request, so I can also cancel those tile requests that are not useful anymore.
To do so, use a lock to stop the thread just after you launch your async tile request, and unlock it as soon as you have your tile cached.
Sounds that good to you? It worked for me!
Your CATiledLayer should be providing this tile from the previous zoom level as you expect. What are your levelsOfDetail and levelsOfDetailBias set to for the tiled layer?
You have to call setNeedsDisplay or setNeedsDisplayInRect: But the problem is if you call this then it will redraw every tile in the scrollView. So try using subclass of UIView instead of CATiledLayer subclass, and implement TiledView (subclass of UIView) like this,
+ (Class) layerClass {
return [CATiledLayer class];
}
-(void)drawRect:(CGRect)r {
CGRect tile = r;
int x = tile.origin.x/TILESIZE;
int y = tile.origin.y/TILESIZE;
NSString *tileName = [NSString stringWithFormat:#"Shed_1000_%i_%i", x, y];
NSString *path =
[[NSBundle mainBundle] pathForResource:tileName ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
[image drawAtPoint:tile.origin];
// uncomment the following to see the tile boundaries
/*
UIBezierPath* bp = [UIBezierPath bezierPathWithRect: r];
[[UIColor whiteColor] setStroke];
[bp stroke];
*/
}
and
for scrollView,
UIScrollView* sv = [[UIScrollView alloc] initWithFrame:
[[UIScreen mainScreen] applicationFrame]];
sv.backgroundColor = [UIColor whiteColor];
self.view = sv;
CGRect f = CGRectMake(0,0,3*TILESIZE,3*TILESIZE);
TiledView* content = [[TiledView alloc] initWithFrame:f];
float tsz = TILESIZE * content.layer.contentsScale;
[(CATiledLayer*)content.layer setTileSize: CGSizeMake(tsz, tsz)];
[self.view addSubview:content];
[sv setContentSize: f.size];