Best core-graphics questions in November 2011

iOS: what's the fastest, most performant way to make a screenshot programatically?

14 votes

in my iPad app, I'd like to make a screenshot of a UIView taking a big part of the screen. Unfortunately, the subviews are pretty deeply nested, so it takes to long to make the screenshot and animate a page curling afterwards.

Is there a faster way than the "usual" one?

UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();

If possible, I'd like to avoid caching or restructuring my view.

Thanks a lot for your help!

I've found the answer for your question in the Technical Q&A of iOS Developer Library, so here it goes:

Every UIWindow (inherited from UIView) and UIView is backed by a CALayer. The CALayer/-renderInContext: method lets you render a layer and its sublayers to a graphics context. So, to grab a snapshot of the entire screen, you can iterate through each window on the screen and render its layer hierarchy to a destination context. Once finished you can get the screenshot image via the UIGraphicsGetImageFromCurrentImageContext function, as shown below.

To use Core Animation APIs, you need to add the QuartzCore framework in your Xcode project and include the QuartzCore header as shown in the following listing:


Listing 1: Include the QuartzCore header

#import <QuartzCore/QuartzCore.h>

Listing 2: Get a screenshot image

- (UIImage*)screenshot 
{
    // Create a graphics context with the target size
    // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
    // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
    CGSize imageSize = [[UIScreen mainScreen] bounds].size;
    if (NULL != UIGraphicsBeginImageContextWithOptions)
        UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
    else
        UIGraphicsBeginImageContext(imageSize);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Iterate over every window from back to front
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) 
    {
        if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen])
        {
            // -renderInContext: renders in the coordinate space of the layer,
            // so we must first apply the layer's geometry to the graphics context
            CGContextSaveGState(context);
            // Center the context around the window's anchor point
            CGContextTranslateCTM(context, [window center].x, [window center].y);
            // Apply the window's transform about the anchor point
            CGContextConcatCTM(context, [window transform]);
            // Offset by the portion of the bounds left of and above the anchor point
            CGContextTranslateCTM(context,
                                  -[window bounds].size.width * [[window layer] anchorPoint].x,
                                  -[window bounds].size.height * [[window layer] anchorPoint].y);

            // Render the layer hierarchy to the current context
            [[window layer] renderInContext:context];

            // Restore the context
            CGContextRestoreGState(context);
        }
    }

    // Retrieve the screenshot image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}

Hope this helps you.

How to change a particular color in an image?

13 votes

My question is if I have a Lion image just I want to change the color of the lion alone not the background color. For that I referred this SO question but it turns the color of whole image. Moreover the image is not looking great. I need the color change like photoshop. whether it is possible to do this in coregraphics or I have to use any other library.

EDIT : I need the color change to be like iQuikColor app

enter image description here

Here is the sketch of a possible solution using OpenCV:

  • Convert the image from RGB to HSV using cvCvtColor (we only want to change the hue).
  • Isolate a color with cvThreshold specifying a certain tolerance (you want a range of colors, not one flat color).
  • Discard areas of color below a minimum size using a blob detection library like cvBlobsLib. This will get rid of dots of the similar color in the scene.
  • Mask the color with cvInRangeS and use the resulting mask to apply the new hue.
  • cvMerge the new image with the new hue with an image composed by the saturation and brightness channels that you saved in step one.

There are several OpenCV iOS ports in the net, eg: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ I haven't tried this myself, but seems a good research direction.