Fast screenshot ios - ios

In my project I have to make a screenshot of the screen and apply blur to create the effect of frosted glass. Content can be moved under the glass and blured picture changed.
I'v used Accelerate.framework to speedup blurring, also i,v used OpenGL to draw CIImage directly to GLView.
Now I'm looking for a way to optimize getting screenshot of the screen.
I use this method to get screenshot of some area at the bottom of the screen:
CGSize size = CGSizeMake(rect.size.width, rect.size.height);
// get screenshot of self.view
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpaceRef, kCGImageAlphaPremultipliedFirst);
CGContextClearRect(ctx, rect);
CGColorSpaceRelease(colorSpaceRef);
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
CGContextSetShouldAntialias(ctx, NO);
CGContextSetAllowsAntialiasing(ctx, NO);
CGContextTranslateCTM(ctx, 0.0, someView.frame.size.height);
CGContextScaleCTM(ctx, 1, -1);
//add mask
CGImageRef maskImage = [UIImage imageNamed:#"mask.png"].CGImage;
CGContextClipToMask(ctx, rect, maskImage);
[someView.layer renderInContext:ctx];
//get screenshot image
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
It works fine and fast if self.view has 1-2 subviews, but if there are several subviews (or it is tableview), then everything starts to slow down.
So i try to find a fast way to get pixels from some rect on screen. Maybe using a low-level API.

if just perform some animations , try this way , called -snapshotViewAfterScreenUpdates: or -resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets: method which are UIView provided , these method return UIView object without rendering into a bitmap image , so it is a more efficient .

Related

Drawing a circular image on top of another image - UIGraphicsBeginImageContextWithOptions

I've been struggling with this method for a while. I am drawing an avatar on top of another image. The user picture I want to be a circle, however I can't seem to figure out how. The user picture is a UIImage and not a UIImageView. I am aware of how to make a circle if it is an imageview. Below is the code. There might be a better approach.
-(UIImage *)drawImage:(UIImage*)pinImage withBadge:(UIImage *)user{
UIGraphicsBeginImageContextWithOptions(pinImage.size, NO, 0.0f);
[pinImage drawInRect:CGRectMake(0, 0, pinImage.size.width, pinImage.size.height)];
[user drawInRect:CGRectMake(20.0, 10.0, user.size.width/2, user.size.height/2)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
The result is good, but the user image is still square, it is not circle. I have tried making the add the User image to a UIImageView, transform it to a circle, and then use it in the method by calling yourImageView.image, but no luck. I also tried numerous other ways. My logic is more than likely incorrect.
The desired outcome is a rounded image place on top of a pin/annotation. Where the black dot would be an image (a bigger circle than this).
You can clip the image context to the path of an image
// Start the image context
UIGraphicsBeginImageContextWithOptions(pinImage.size, NO, 0.0);
UIImage *resultImage = nil;
// Get the graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw the first image
[pinImage drawInRect:CGRectMake(0, 0, pinImage.size.width, pinImage.size.height)];
// Get the frame of the second image
CGRect rect = CGRectMake(20.0, 10.0, user.size.width/2, user.size.height/2)
// Add the path of an ellipse to the context
// If the rect is a square the shape will be a circle
CGContextAddEllipseInRect(context, rect);
// Clip the context to that path
CGContextClip(context);
// Do the second image which will be clipped to that circle
[user drawInRect:rect];
// Get the result
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
// End the image context
UIGraphicsEndImageContext();
Create a circular path and then clip to that?
CGContextAddArc(ctx, ....);
CGContextClip(ctx);

Merge two PNG UIImages in iOS without losing transparency

I have two png format images and both have transparency defined. I need to merge these together into a new png image but without losing any of the transparency in the result.
+(UIImage *) combineImage:(UIImage *)firstImage colorImage:(UIImage *)secondImage
{
UIGraphicsBeginImageContext(firstImage.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, firstImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGRect rect = CGRectMake(0, 0, firstImage.size.width, firstImage.size.height);
// draw white background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeDarken);
[[UIColor whiteColor] setFill];
CGContextFillRect(context, rect);
CGContextSaveGState(context);
CGContextRestoreGState(context);
// draw original image
CGContextSetBlendMode(context, kCGBlendModeDarken);
CGContextDrawImage(context, rect, firstImage.CGImage);
// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeDarken);
//CGContextSetAlpha(context, .85);
[[UIColor colorWithPatternImage:secondImage] setFill];
CGContextFillRect(context, rect);
CGContextSaveGState(context);
CGContextRestoreGState(context);
// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, firstImage.CGImage);
// image drawing code here
CGContextRestoreGState(context);
UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return coloredImage;
}
needed any help to improve my code in performance.
Thanks in advance
First of all, those calls to CGContextSaveGState and CGContextRestoreGState, one after the other with nothing in between, isn't doing anything for you. See this other answer for an explanation of what CGContextSaveGState and CGContextRestoreGState do: CGContextSaveGState vs UIGraphicsPushContext
Now, it's not 100% clear to me what you mean by "merging" the images. If you just want to draw one on top of the other, and blend their colors using a standard blending mode then you just need to change those blend mode calls to pass kCGBlendModeNormal (or just leave out the calls to CGContextSetBlendMode entirely. If you want to mask the second image by the first image's alpha value then you should draw the second image with the normal blend mode, then switch to kCGBlendModeDestinationIn and draw the first image.
I'm afraid I'm not really sure what you're trying to do with the image tinting code in the middle, but my instinct is that you won't end up needing it. You should be able to get most merging effects by just drawing one image, then setting the blending mode, then drawing the other image.
Also, the code you've got there under the comment "draw white background to preserve color of transparent pixels" might draw white through the whole image, but it certainly doesn't preserve the color of transparent pixels, it makes those pixels white! You should remove that code too unless you really want your "transparent" color to be white.
Used the code given in Vinay's question and Aaron's comments to develop this hybrid that overlays any number of images:
/**
Returns the images overplayed atop each other according to their array position, with the first image being bottom-most, and the last image being top-most.
- parameter images: The images to overlay.
- parameter size: The size of resulting image. Any images not matching this size will show a loss in fidelity.
*/
func combinedImageFromImages(images: [UIImage], withSize size: CGSize) -> UIImage
{
// Setup the graphics context (allocation, translation/scaling, size)
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, 0, size.height)
CGContextScaleCTM(context, 1.0, -1.0)
let rect = CGRectMake(0, 0, size.width, size.height)
// Combine the images
for image in images {
CGContextDrawImage(context, rect, image.CGImage)
}
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return combinedImage
}

MonoTouch convert color UIImage with alpha to grayscale and blur?

I am trying to find a recipe for producing a a blurred grayscale UIImage from a color PNG and alpha. There are recipes out there in ObjC but MonoTouch does not bind the CGRect functions so not sure how to do this. Any ideas?
Here is one ObjC example of grayscale:
(UIImage *)convertImageToGrayScale:(UIImage *)image
{
// Create image rectangle with current image width/height
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
// Grayscale color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
// Create bitmap content with current image size and grayscale colorspace
CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
CGContextDrawImage(context, imageRect, [image CGImage]);
// Create bitmap image info from pixel data in current context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
// Create a new UIImage object
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
// Release colorspace, context and bitmap information
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
CFRelease(imageRef);
// Return the new grayscale image
return newImage;
}
Monotouch does not bind the CGRect functions so not sure how to do this.
When using MonoTouch CGRect is mapped to RectangleF. A lot of extension methods exists that should map to every function provided by GCRect. You should not have any problem in porting ObjectiveC code using them.
If something is missing please fill a bug report # http://bugzilla.xamarin.com and we'll fix it asap (and provide a workaround when possible).
There are recipes out there in ObjC but MonoTouch
If you have links then please edit your question and add them. That will make it easier to help you :)
UPDATE
Here's a, line-by-line, C# translation of your example. It seems to works for me (and it's much easier to my eyes than Objective-C ;-)
UIImage ConvertToGrayScale (UIImage image)
{
RectangleF imageRect = new RectangleF (PointF.Empty, image.Size);
using (var colorSpace = CGColorSpace.CreateDeviceGray ())
using (var context = new CGBitmapContext (IntPtr.Zero, (int) imageRect.Width, (int) imageRect.Height, 8, 0, colorSpace, CGImageAlphaInfo.None)) {
context.DrawImage (imageRect, image.CGImage);
using (var imageRef = context.ToImage ())
return new UIImage (imageRef);
}
}
I wrote a native port of the blur and tint UIImage categories from WWDC for Monotouch.
https://github.com/lipka/MonoTouch.UIImageEffects
Sample code for tint and blur:
UIColor tintColor = UIColor.FromWhiteAlpha (0.11f, 0.73f);
UIImage yourImage;
yourImage.ApplyBlur (20f /*blurRadius*/, tintColor, 1.8f /*deltaSaturationFactor*/, null);

Convert PDF to UIImageView

I've found some code which gives me a UIImage out of a PDF-File. It works, but I have two questions:
Is there a possibility to achieve a better quality of the UIImage? (See Screenshot)
I only see the first page in my UIImageView. Do I have to embed the file in a UIScrollView to be complete?
Or is it better to render just one page and use buttons to navigate through the pages?
P.S. I know that UIWebView can display PDF-Pages with some functionalities but I need it as a UIImage or at least in a UIView.
Bad quality Image:
Code:
-(UIImage *)image {
UIGraphicsBeginImageContext(CGSizeMake(280, 320));
CGContextRef context = UIGraphicsGetCurrentContext();
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("ls.pdf"), NULL, NULL);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CGContextTranslateCTM(context, 0.0, 320);
CGContextScaleCTM(context, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 4);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, CGRectMake(0, 0, 280, 320), 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultingImage;
}
I know i'm a little late here, but i hope i can help someone else looking for an answer.
As to the questions asked:
I'm afraid the only way to achieve a better image quality is to render a bigger image, and letting the UIImageView resize it for you. I don't think you can set the resolution, but using a bigger image may be a good choice. It won't take too long for the page to render, and the image will have a better quality. PDF files are rendered on demand depending on the zoom level, that's why they seem to have "better quality".
As to rendering all the pages, you can get the number of pages in the document calling CGPDFDocumentGetNumberOfPages( pdf ) and using a simple for loop you can concat all the images generated in one single image. For displaying it, use the UIScrollVIew.
In my opinion, this approach is better than the above, but you should try to optimize it, for example rendering always the current, the previous and the next page. For nice scrolling transition effects, why not use a horizontal UIScrollView.
For more generic rendering code, i always do the rotation like this:
int rotation = CGPDFPageGetRotationAngle(page);
CGContextTranslateCTM(context, 0, imageSize.height);//moves up Height
CGContextScaleCTM(context, 1.0, -1.0);//flips horizontally down
CGContextRotateCTM(context, -rotation*M_PI/180);//rotates the pdf
CGRect placement = CGContextGetClipBoundingBox(context);//get the flip's placement
CGContextTranslateCTM(context, placement.origin.x, placement.origin.y);//moves the the correct place
//do all your drawings
CGContextDrawPDFPage(context, page);
//undo the rotations/scaling/translations
CGContextTranslateCTM(context, -placement.origin.x, -placement.origin.y);
CGContextRotateCTM(context, rotation*M_PI/180);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageSize.height);
Steipete already mentioned setting the white background:
CGContextSetRGBFillColor(context, 1, 1, 1, 1);
CGContextFillRect(context, CGRectMake(0, 0, imageSize.width, imageSize.height));
So the last thing to keep in mind is when exporting an image, set the quality to the maximum. For example:
UIImageJPEGRepresentation(image, 1);
What are you doing with the CGContextTranslateCTM(context, 0.0, 320); call?
You should extract the proper metrics form the pdf, with code like this:
cropBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
rotate = CGPDFPageGetRotationAngle(page);
Also, as you see, the pdf might has rotation info, so you need to use the CGContextTranslateCTM/CGContextRotateCTM/CGContextScaleCTM depending on the angle.
You also might wanna clip any content that is outside of the CropBox area, as pdf has various viewPorts that you usually don't wanna display (e.g. for printers so that seamless printing is possible) -> use CGContextClip.
Next, you're forgetting that the pdf reference defines a white background color. There are a lot of documents out there that don't define any background color at all - you'll get weird results if you don't draw a white background on your own --> CGContextSetRGBFillColor & CGContextFillRect.

Draw background image using CGContextDrawImage

I want to draw on a UIView that has a background image that I set using the code below:
- (void)setBackgroundImageFromData:(NSData *)imageData {
UIImage *image = [UIImage imageWithData:imageData];
int width = image.size.width;
int height = image.size.height;
CGSize size = CGSizeMake(width, height);
CGRect imageRect = CGRectMake(0, 0, width, height);
UIGraphicsBeginImageContext(size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(currentContext, 0, height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextDrawImage(currentContext, imageRect, image.CGImage);
UIGraphicsEndImageContext();
}
The initial view is created using the code from Apple's GLPaint example. For the life of me, that background image is not shown. What am I missing?
Thanks!
You create a UIImage, and an imageRect successfully. You then begin a image context to draw the image to, draw the image to the context, and end the context. The problem is that you just allow the context to expire without doing anything to it.
In UIKit you don't push new visuals upwards, you wait until requested to draw. Internal mechanisms cache your images and use them to move things about and otherwise redraw the screen at the usual 60fps.
If this is a custom UIView subclass, then you probably want to keep hold of the UIImage and composite it as part of your drawRect:. You can set the contents of your UIView as having changed by calling setNeedsRedraw — you'll then be asked to redraw your contents at some point in the future.
If this isn't a custom subclass, then the easiest thing to do is to wrap this view in an outer view and add a UIImageView behind it, to which you can set the UIImage.

Resources