I'm trying to overlay picture taken from camera with some other preset images. Problem is, picture from camera might be 8MP which is huge in term of memory usage. Some of the overlays might try to cover the whole image.
I tried multiple way to merge them all into one image.
UIGraphicsBeginImageContextWithOptions(_imageView.image.size, NO, 1.0f);
[_containerView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
or
CGContextDrawImage(UIGraphicsGetCurrentContext(), (CGRect){CGPointZero, _imageView.image.size}, _imageView.image.CGImage);
CGContextDrawImage(UIGraphicsGetCurrentContext(), (CGRect){CGPointZero, _imageView.image.size}, *other image views*);
or
UIImage* image = _imageView.image;
CGImageRef imageRef = image.CGImage;
size_t imageWidth = (size_t)image.size.width;
size_t imageHeight = (size_t)image.size.height;
CGContextRef context = CGBitmapContextCreate(NULL, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef));
CGRect rect = (CGRect){CGPointZero, {imageWidth, imageHeight}};
CGContextDrawImage(context, rect, imageRef);
CGContextDrawImage(context, rect, **other images**);
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage* resultImage = nil;
NSURL* url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"x.jpg"]];
CFURLRef URLRef = CFBridgingRetain(url);
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(URLRef, kUTTypeJPEG, 1, NULL);
if (destination != NULL)
{
CGImageDestinationAddImage(destination, newImageRef, NULL);
if (CGImageDestinationFinalize(destination))
{
resultImage = [[UIImage alloc] initWithContentsOfFile:url.path];
}
CFRelease(destination);
}
CGImageRelease(newImageRef);
all of these work but essentially, doubling the current memory usage.
Is there anyway to compose them all together without the need to create new context? Maybe save all images in file system and doing the merging there without actually consuming tons of memory? Or maybe even rendering into filesystem tile per tile?
Any suggestion or pointer where do I go from this?
Thanks
Check out the following code. You can send multiple images to the following method, however as you are facing memory issues, I suggest you to call the same method repetitively to merge multiple images. This process might take more time though.
-(CGImageRef )mergedImageFromImageOne:(UIImage *)imageOne andImageTwo:(UIImage *)imageTwo
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGSize imageSize = imageOne.size;
UIGraphicsBeginImageContext(sizeVideo);
[imageOne drawInRect:CGRectMake(0,0,imageSize.width,imageSize.height)];
[imageTwo drawInRect:CGRectMake(0,0,imageSize.width,imageSize.height) alpha:1];
CGImageRef imageRefNew = CGImageCreateWithImageInRect(UIGraphicsGetImageFromCurrentImageContext().CGImage, CGRectMake(0,0,imageOne.width,imageOne.height));
UIGraphicsEndImageContext();
[pool release];
return imageRefNew;
}
Hope this helps.
Related
I am not the best graphic designer, but need to setup few icons. I made them with gimp with options like Border, etc. All are primary designed in 1000x1000px and used in objective-c code in UIImageView.
I am wondering why resized icon look that terrible. Any suggestions?
In app:
http://s14.postimg.org/k2g9uayld/Screen_Shot_2014_12_18_at_11_22_53.png
http://s14.postimg.org/biwvwjq8x/Screen_Shot_2014_12_18_at_11_23_02.png
Original image:
Can't Post more than 2 links so: s17.postimg.org/qgi4p80an/fav.png
I dont think that matters but
UIImage *image = [UIImage imageNamed:#"fav.png"];
image = [UIImage imageWithCGImage:[image CGImage] scale:25 orientation:UIImageOrientationUp];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:image];
But one of images has been set up in storyboard and effect is the same.
[self.favO.layer setMinificationFilter:kCAFilterTrilinear];
[self.favO setImage:[self resizeImage:[UIImage imageNamed:#"fav.png"] newSize:CGSizeMake(35,35)]
forState:UIControlStateNormal];
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = image.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(context, flipVertical);
CGContextDrawImage(context, newRect, imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}
This fixed problem. This problem occurs while UIImageView takes care of resizing.
To avoid this kind of behavior I suggest you make SVGs instead of png files. Using the lib SVGKit you can then resize it to your heart content. Since SVG format is a vectorial format there won't be any loss in your scaling.
https://github.com/SVGKit/SVGKit
EDIT:
To add up to this solution, your PNG file at 1000X1000px must be really heavy, on the other hand a SVG file is a text file so it makes it very lightweight
I am rendering PDFs using core graphics, CGContextDrawPDFPage function. I am trying to show the PDF pages in image views. The pages are getting rendered perfectly. But some pages are not in correct orientation. Some are flipped right, upside-down. This happens for only a few PDFs/pages, not for all. The following the code snippet that renders the PDF pages. I've seen a lot of posts online, but all of them are failing in some way. Also I am not much familiar with the core graphics.
CGImageRef imageRef = NULL;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bmi = (kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
CGContextRef context = CGBitmapContextCreate(NULL, target_w, target_h, 8, 0, rgb, bmi);
if (context != NULL) {
CGRect thumbRect = CGRectMake(0.0f, 0.0f, target_w, target_h);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(context, thumbRect);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGRect mediaRect = CGPDFPageGetBoxRect(thePDFPageRef, kCGPDFCropBox);
CGContextScaleCTM(context, target_w / mediaRect.size.width, target_h / mediaRect.size.height);
CGContextTranslateCTM(context, -mediaRect.origin.x, -mediaRect.origin.y);
CGContextDrawPDFPage(context, thePDFPageRef);
imageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
}
CGColorSpaceRelease(rgb);
if (imageRef != NULL) {
UIImage *image = [UIImage imageWithCGImage:imageRef scale:UIScreen.mainScreen.scale orientation:UIImageOrientationUp];
CFRelease(imageRef);
}
Please suggest me the perfect way to render PDFs.
I'm not sure if this help you, because I'm not entirely sure, what are you trying to capture in your pdf.
However, when I did it, I needed to do pdf from my webview. For this, you need to
#import <QuartzCore/QuartzCore.h>
and then
CGRect frame = myWebView.frame;
frame.size.height = 1;
myWebView.frame = frame;
CGSize fittingSize = [myWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
myWebView.frame = frame;
UIGraphicsBeginImageContextWithOptions(fittingSize, YES, 2);
[myWebView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *myImg = [[UIImageView alloc] initWithImage:viewImage];
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, myImg.bounds, nil);
UIGraphicsBeginPDFPage();
[myImg.layer renderInContext:UIGraphicsGetCurrentContext()];
UIGraphicsEndPDFContext();
I 've a PNG loaded into an UIImage. I want to get a portion of the image based on a path (i.e. it might not be a rectangular). Say, it might be some shape with arcs, etc. Like a drawing path.
What would be the easiest way to do that?
Thanks.
I haven't run this, so it may not be perfect but this should give you an idea.
UIImage *imageToClip = //get your image somehow
CGPathRef yourPath = //get your path somehow
CGImageRef imageRef = [imageToClip CGImage];
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGImageGetColorSpace(imageRef), kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextAddPath(context, yourPath);
CGContextClip(context);
CGImageRef clippedImageRef = CGBitmapContextCreateImage(context);
UIImage *clippedImage = [UIImage imageWithCGImage:clippedImageRef];//your final, masked image
CGImageRelease(clippedImageRef);
CGContextRelease(context);
The easiest way to add a category to the UIImage with follow method:
-(UIImage *)scaleToRect:(CGRect)rect{
// Create a bitmap graphics context
// This will also set it as the current context
UIGraphicsBeginImageContext(size);
// Draw the scaled image in the current context
[self drawInRect:rect];
// Create a new image from current context
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// Pop the current context from the stack
UIGraphicsEndImageContext();
// Return our new scaled image
return scaledImage;
}
I am trying to do a pixel by pixel comparison of two UIImages and I need to retrieve the pixels that are different. Using this Generate hash from UIImage I found a way to generate a hash for a UIImage. Is there a way to compare the two hashes and retrieve the different pixels?
If you want to actually retrieve the difference, the hash cannot help you. You can use the hash to detect the likely presence of differences, but to get the actual differences, you have to use other techniques.
For example, to create a UIImage that consists of the difference between two images, see this accepted answer in which Cory Kilgor's illustrates the use of CGContextSetBlendMode with a blend mode of kCGBlendModeDifference:
+ (UIImage *) differenceOfImage:(UIImage *)top withImage:(UIImage *)bottom {
CGImageRef topRef = [top CGImage];
CGImageRef bottomRef = [bottom CGImage];
// Dimensions
CGRect bottomFrame = CGRectMake(0, 0, CGImageGetWidth(bottomRef), CGImageGetHeight(bottomRef));
CGRect topFrame = CGRectMake(0, 0, CGImageGetWidth(topRef), CGImageGetHeight(topRef));
CGRect renderFrame = CGRectIntegral(CGRectUnion(bottomFrame, topFrame));
// Create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if(colorSpace == NULL) {
printf("Error allocating color space.\n");
return NULL;
}
CGContextRef context = CGBitmapContextCreate(NULL,
renderFrame.size.width,
renderFrame.size.height,
8,
renderFrame.size.width * 4,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
if(context == NULL) {
printf("Context not created!\n");
return NULL;
}
// Draw images
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, CGRectOffset(bottomFrame, -renderFrame.origin.x, -renderFrame.origin.y), bottomRef);
CGContextSetBlendMode(context, kCGBlendModeDifference);
CGContextDrawImage(context, CGRectOffset(topFrame, -renderFrame.origin.x, -renderFrame.origin.y), topRef);
// Create image from context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage * image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
return image;
}
I used the timing profile tool to identify that 95% of the time is spent calling the function CGContextDrawImage.
In my app there are a lot of duplicate images repeatably being chopped from a sprite map and drawn to the screen. I was wondering if it was possible to cache the output of CGContextDrawImage in an NSMutableDictionay, then if the same sprite is requested again it can be just pull it from the cache rather than doing all the work of clipping and rendering it again. This is what i’ve got but I have not been to successful:
Definitions
if(cache == NULL) cache = [[NSMutableDictionary alloc]init];
//Identifier based on the name of the sprite and location within the sprite.
NSString* identifier = [NSString stringWithFormat:#"%#-%d",filename,frame];
Adding to cache
CGRect clippedRect = CGRectMake(0, 0, clipRect.size.width, clipRect.size.height);
CGContextClipToRect( context, clippedRect);
//create a rect equivalent to the full size of the image
//offset the rect by the X and Y we want to start the crop
//from in order to cut off anything before them
CGRect drawRect = CGRectMake(clipRect.origin.x * -1,
clipRect.origin.y * -1,
atlas.size.width,
atlas.size.height);
//draw the image to our clipped context using our offset rect
CGContextDrawImage(context, drawRect, atlas.CGImage);
[cache setValue:UIGraphicsGetImageFromCurrentImageContext() forKey:identifier];
UIGraphicsEndImageContext();
Rendering a cached sprite
There is probably a better way to render CGImage which is my ultimate caching goal but at the moment I’m just looking to successfully render the cached image out however this has not been successful.
UIImage* cachedImage = [cache objectForKey:identifier];
if(cachedImage){
NSLog(#"Cached %#",identifier);
CGRect imageRect = CGRectMake(0,
0,
cachedImage.size.width,
cachedImage.size.height);
if (NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0);
else
UIGraphicsBeginImageContext(imageRect.size);
//Use draw for now just to see if the image renders out ok
CGContextDrawImage(context, imageRect, cachedImage.CGImage);
UIGraphicsEndImageContext();
}
Yes it's possible to cache a rendered image. Below is a sample of how it's done:
+ (UIImage *)getRenderedImage:(UIImage *)image targetSize:(CGSize)targetSize
{
CGRect targetRect = CGRectIntegral(CGRectMake(0, 0, targetSize.width, targetSize.height)); // should be used by your drawing code
CGImageRef imageRef = image.CGImage; // should be used by your drawing code
UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// TODO: draw and clip your image here onto context
// CGContextDrawImage CGContextClipToRect calls
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}
This way, you get a rendered copy of the resource image. Because during rendering, you have the context, you are free to do anything you want. You just need to determine the output size beforehand.
The resulting image is just an instance of UIImage that you can put into NSMutableDictionary for later use.