Rendering views, scaled #2x, renderInContext, iPhone - iphone

I have a view (called outPutView) that contains graphics, like uIImageViews and labels. I need to render an image of the outPutView and it's sub-views. I am using renderInContext:UIGraphicsGetCurrentContext() to do so. Works fine, except that I need to scale the views. I am using a transform on the outPutView. This successfully scales the view and it's sub-views, but the transform does not render. While the views are scaled onscreen. the final render displays the vies at their original size, while the render context is at the target size (here #2x iPhone view size).
Thanks for reading!!
[outPutView setTransform:CGAffineTransformMake(2, 0, 0, 2, 0, 0)];
CGSize renderSize = CGSizeMake(self.view.bounds.size.width*2, self.view.bounds.size.height*2);
UIGraphicsBeginImageContext(renderSize);
[[outPutView layer] renderInContext:UIGraphicsGetCurrentContext()];
renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

I've just made this work, although in the opposite direction (scaling down). Here's a summary of the relevant code:
// destination size is half of self (self is a UIView)
float rescale = 0.5;
CGSize resize = CGSizeMake(self.width * rescale, self.height * rescale);
// make the destination context
UIGraphicsBeginImageContextWithOptions(resize, YES, 0);
// apply the scale to dest context
CGContextScaleCTM(UIGraphicsGetCurrentContext(), rescale, rescale);
// render self into dest context
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
// grab the resulting UIImage
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
To scale up instead of down, it should be fine to have rescale = 2.

I solved this by re-ordering my views. Actually adding another view between the output view: the view that the rendered context is taken from, and the view that is scaled via transform. It worked, but I have no idea why at this point. Any thoughts on this would be appreciated. Thanks for reading.

Related

Draw image constrain aspects

I have an imageview, the image within this imageview is for you to choose from the photoroll. I also have a button, when you click this button, there's an image added to a view with the addSubview code. This piece of image is draggable, resizeable and rotatable.
One problem, when I finish the image I use the method drawInRect. This draws all the layers onto eachother and creates an image. However the layers are on the wrong place and are the wrong size. It's also never rotated. I don't know how to fix this, the piece of code is beneath this text. Is it possible to keep the original image size and still have the layers drawn on the same place I drag them onto the imageview, if not how do I create a new size for this and have the result I want. And how do I draw the image rotated.
UIGraphicsBeginImageContext(imageView2.image.size);
// Draw image1
[imageView2.image drawInRect:CGRectMake(0, 0, imageView2.image.size.width, imageView2.image.size.height)];
// Draw image2
for(UIImageView *viewsSub in [self.imageViewer subviews])
{
[viewsSub.image drawInRect:CGRectMake(viewsSub.frame.origin.x, viewsSub.frame.origin.y, viewsSub.frame.size.width, viewsSub.frame.size.height)];
}
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
pld.imageChosen2 = resultingImage;
UIGraphicsEndImageContext();
So you want something like taking a "screenshot" of your actual imageview (with subviews included), don't you?
I used this piece of code to do something similar, but don't know if would work for you.
- (UIImage *)screenshot {
CGFloat scale = [UIScreen screenScale];
if(scale > 1.5) {
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, scale);
} else {
UIGraphicsBeginImageContext(self.frame.size);
}
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenshot;
}
You should add this method inside your imageview (the one that contains all the subviews you're adding).

Scale and Save UIImage from Photo Library in iPhone?

i am trying to scale down a image i get from photo library on touchesmoved by user in a similar way like when we take picture with camera using UIImagepicker setEditing to Yes method(or like the camera app).
I am trying to use the following method passing in some parameters based on touchesmoved but i am not getting the desired effect? what am i possibly doing wrong??
-(UIImage*)scaleToSize:(UIImage *)img:(CGSize)size
{
// Create a bitmap graphics context
// This will also set it as the current context
UIGraphicsBeginImageContext(size);
// Draw the scaled image in the current context
[img drawInRect:CGRectMake(0, 0, size.width, size.height)];
// 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;
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UIImage *img = [self scaleToSize:imgView.image:CGSizeMake(touch1.x,touch1.y)];
imgView.image=img;
}
also how can i save the scaled image once somehow i scale it??
Building on your comment, the image will distort as it will draw the image into the rectangle specified and if the new dimensions are not the same aspect ratio (the width / height) as the original image then it will appear distorted.
You need some logic to ensure that your new width and height have the same aspect ratio, for example:
CGFloat newHeight = imageView.frame.size.height * size.width / imageView.frame.size.width;
If you make your Graphics context size.width and newHeight and then draw your image into this rect it will maintain the aspect ratio.
You will likely want to put some extra logic in there to either create a new width given the height or new height given the width, depending on which dimension was the largest change.
Hope this helps,
Dave

How to I rotate UIImageView by 90 degrees inside a UIScrollView with correct image size and scrolling?

I have an image inside an UIImageView which is within a UIScrollView. What I want to do is rotate this image 90 degrees so that it is in landscape by default, and set the initial zoom of the image so that the entire image fits into the scrollview and then allow it to be zoomed up to 100% and back down to minimum zoom again.
This is what I have so far:
self.imageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
float minimumScale = scrollView.frame.size.width / self.imageView.frame.size.width;
scrollView.minimumZoomScale = minimumScale;
scrollView.zoomScale = minimumScale;
scrollView.contentSize = CGSizeMake(self.imageView.frame.size.height,self.imageView.frame.size.width);
The problem is that if I set the transform, nothing shows up in the scrollview. However if I commented out the transform, everything works except the image is not in the landscape orientation that I want it to be!
If I apply the transform and remove the code that sets the minimumZoomScale and zoomScale properties, then the image shows up in the correct orientation, however with the incorrect zoomScale and seems like the contentSize property isn't set correctly either - since the doesn't scroll to the edge of the image in the left/right direction, however does top and bottom but much over the edge.
NB: image is being loaded from a URL
Maybe rotating the image itself fits your needs:
UIImage* rotateUIImage(const UIImage* src, float angleDegrees) {
UIView* rotatedViewBox = [[UIView alloc] initWithFrame: CGRectMake(0, 0, src.size.width, src.size.height)];
float angleRadians = angleDegrees * ((float)M_PI / 180.0f);
CGAffineTransform t = CGAffineTransformMakeRotation(angleRadians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
[rotatedViewBox release];
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
CGContextRotateCTM(bitmap, angleRadians);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), [src CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I believe the easiest way (and thread safe too) is to do:
//assume that the image is loaded in landscape mode from disk
UIImage * LandscapeImage = [UIImage imageNamed: imgname];
UIImage * PortraitImage = [[UIImage alloc] initWithCGImage: LandscapeImage.CGImage
scale: 1.0
orientation: UIImageOrientationLeft];
Any calculations that you do based on the imageView's frame should probably be done before you apply any transformations to it. But I would actually suggest doing those calculations based on the size of the UIImage, not the UIImageView. Then set both the UIImageView's frame and the UIScrollView's contentSize based on that.
Max's suggestion is a good one, although with a larger image it could be a performance killer. Are you displaying this image from your app's resources? If so, why not just rotate the images before you even build the app?
There's a much easier solution that is also faster, just do this:
- (void) imageRotateTapped:(id)sender
{
[UIView animateWithDuration:0.33f animations:^()
{
self.imageView.transform = CGAffineTransformMakeRotation(RADIANS(self.rotateDegrees += 90.0f));
self.imageView.frame = self.imageView.superview.bounds; // change this to whatever rect you want
}];
}
When the user is done, you will need to actually create a new rotated image, but that is very easy to do.
I was using the accepted answer for a while until we noticed that non-square rotations based on images taken directly from the camera seemed stretched (they were rotated as desired, just the frame width/height wasn't adjusted).
Great explanation/post here from Trevor: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
In the end, it was a very simple import of Trevor's code which uses categories to add a resizedImage:interpoationQuality method to UIImage. So yeah, user beware, if it still works for you, great. But if it doesn't, I'd take a look at the library instead.

Getting a resized screenshot from a UIView

I'm trying to take a screenshot of a UIView shrunk down to thumbnail size with the following code,
UIGraphicsBeginImageContext(size);
[canvas.layer renderInContext:UIGraphicsGetCurrentContext()];
result = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
The above code will simply grab the top left portion of the view in the original unshrunk size instead.
I'm sure I've done this before, but I just can't get it working. Anyone know what's off here?
Supposing that you have a CGSize origSize which is the original size (e.g. 768x1024) and a CGSize size which is the required size, this can be done like so:
CGFloat scaleX = size.width / origSize.width;
CGFloat scaleY = size.height / origSize.height;
UIGraphicsBeginImageContextWithOptions(origSize, NO, scaleX > scaleY ? scaleY : scaleX);
[canvas.layer renderInContext:UIGraphicsGetCurrentContext()];
result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Note that we're using origSize in the begun context, not size. The scale affects the size as well.
Update (roughly a year later): note that this technique interferes with (or is interefered by) transforms on the UIView being snapshotted. If the above is not working and you're doing scale transforms on the view (or its layer), you may wanna go with this solution: How to scale down a UIImage and make it crispy / sharp at the same time instead of blurry?
I find that this solution generates thumbnails that are the right size.
let thumbRect = CGRect(x: 0, y: 0, width: 512, height: 666)
UIGraphicsBeginImageContext(thumbSize)
let context = UIGraphicsGetCurrentContext()
self.view.frame = thumbRect
self.view.layer.renderInContext(context)
thumbImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
However, the resized image adopts the trait collection from the original view controller.
So although the size is correct, some auto layout features still end-up visible in the resulting image.

Any quick and dirty anti-aliasing techniques for a rotated UIImageView?

I've got a UIImageView (full frame and rectangular) that i'm rotating with a CGAffineTransform. The UIImage of the UIImageView fills the entire frame. When the image is rotated and drawn the edges appear noticeably jagged. Is there anything I can do to make it look better? It's clearly not being anti-aliased with the background.
The edges of CoreAnimation layers aren't antialiased by default on iOS. However, there is a key that you can set in Info.plist that enables antialiasing of the edges: UIViewEdgeAntialiasing.
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html
If you don't want the performance overhead of enabling this option, a work-around is to add a 1px transparent border around the edge of the image. This means that the 'edges' of the image are no longer on the edge, so don't need special treatment!
New API – iOS 6/7
Also works for iOS 6, as noted by #Chris, but wasn't made public until iOS 7.
Since iOS 7, CALayer has a new property allowsEdgeAntialiasing which does exactly what you want in this case, without incurring the overhead of enabling it for all views in your application! This is a property of CALayer, so to enable this for a UIView you use myView.layer.allowsEdgeAntialiasing = YES.
just add 1px transparent border to your image
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[image drawInRect:CGRectMake(1,1,image.size.width-2,image.size.height-2)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Remember to set the appropriate anti-alias options:
CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);
just add "Renders with edge antialiasing" with YES in plist and it will work.
I would totally recommend the following library.
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
It contains lots of useful extensions to UIImage that solve this problem and also include code for generating thumbnails etc.
Enjoy!
The best way I've found to have smooth edges and a sharp image is to do this:
CGRect imageRect = CGRectMake(0, 0, self.photo.image.size.width, self.photo.image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[self.photo.image drawInRect:CGRectMake(1, 1, self.photo.image.size.width - 2, self.photo.image.size.height - 2)];
self.photo.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Adding the Info.plist key like some people describe has a big hit on performance and if you use that then you're basically applying it to everything instead of just the one place you need it.
Also, don't just use UIGraphicsBeginImageContext(imageRect.size); otherwise the layer will be blurry. You have to use UIGraphicsBeginImageContextWithOptions like I've shown.
I found this solution from here, and it's perfect:
+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame transparentInsets:(UIEdgeInsets)insets {
CGSize imageSizeWithBorder = CGSizeMake(frame.size.width + insets.left + insets.right, frame.size.height + insets.top + insets.bottom);
// Create a new context of the desired size to render the image
UIGraphicsBeginImageContextWithOptions(imageSizeWithBorder, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Clip the context to the portion of the view we will draw
CGContextClipToRect(context, (CGRect){{insets.left, insets.top}, frame.size});
// Translate it, to the desired position
CGContextTranslateCTM(context, -frame.origin.x + insets.left, -frame.origin.y + insets.top);
// Render the view as image
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
// Fetch the image
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup
UIGraphicsEndImageContext();
return renderedImage;
}
usage:
UIImage *image = [UIImage renderImageFromView:view withRect:view.bounds transparentInsets:UIEdgeInsetsZero];