Proper use of UIRectClip to scale UIImage to icon size

Given the UIImage of any dimension, I want to generate a square version of the โ€œiconโ€ size, px pixels to the side, without any distortion (stretching). However, I come across a little grip. Not quite sure where the problem is. Here is what I am doing so far.

First, given the UImage size , I define three things: ratio to use when reducing the image; a delta (difference between the desired icon size and the longest side) and offset (which is used to determine the coordinates of the origin when clipping the image):

  if (size.width > size.height) { ratio = px / size.width; delta = (ratio*size.width - ratio*size.height); offset = CGPointMake(delta/2, 0); } else { ratio = px / size.height; delta = (ratio*size.height - ratio*size.width); offset = CGPointMake(0, delta/2); } 

Now let's say that you have an image of 640 pixels in height and 480 pixels, and we want to get a 50 pixels x 50 pixels icon. Width is greater than height, so our calculations are:

 ratio = 50px / 640px = 0.078125 delta = (ratio * 640px) - (ratio * 480px) = 50px - 37.5px = 12.5px offset = {x=6.25, y=0} 

Next, I create a CGRect rect , which is large enough to crop the icon to the desired size without distortion, plus clipRect for clipping:

 CGRect rect = CGRectMake(0.0, 0.0, (ratio * size.width) + delta, (ratio * size.height) + delta); CGRect clipRect = CGRectMake(offset.x, offset.y, px, px); 

Substituting our values โ€‹โ€‹above, we get:

 rect = origin {x=0.0, y=0.0}, size {width=62.5, height=50.0} clipRect = origin {x=6.25, y=0}, size {width=50.0, height=50.0} 

So, now we have 62.5px 50px wide with a high rectangle for working with it and a clipping rectangle that captures the "middle" part of 50x50.

At the home site! Then we create the image context, draw the UIImage (called myImage here) into the rectangle, set the cropping rectangle, get the (presumably now cropped) image, use it, and finally clear our image context:

  UIGraphicsBeginImageContext(rect.size); [myImage drawInRect:rect]; UIRectClip(clipRect); UIImage *icon = UIGraphicsGetImageFromCurrentImageContext(); // Do something with the icon here ... UIGraphicsEndImageContext(); 

Only one problem: pruning never occurs! I get an image that is 63px wide x 50px wide. :(

Perhaps I am abusing / misunderstanding UIRectClip ? I tried shuffling different things: replacing rect and clipRect , moving UIRectClip to drawInRect:. No dice.

I also tried to find an example of this method on the Internet, but to no avail. For recording, UIRectClip is defined as:

Changes the current cropping path to crossing it with the specified rectangle.

Stirring things around us is a little closer:

 UIGraphicsBeginImageContext(clipRect.size); UIRectClip(rect); [myImage drawInRect:rect]; 

Now we have no distortion, but the cropped image is not focused on the original, as I expected. However, at least the image is 50x50, although the variable names are now dirty due to the shuffle mentioned. (I respectfully leave renaming as an exercise for the reader.)

+7
iphone uiimage icons scale
source share
3 answers

Eureka! I got a little confused. It works:

 CGRect clipRect = CGRectMake(-offset.x, -offset.y, (ratio * size.width) + delta, (ratio * size.height) + delta); UIGraphicsBeginImageContext(CGSizeMake(px, px)); UIRectClip(clipRect); [myImage drawInRect:clipRect]; UIImage *icon = UIGraphicsGetImageFromCurrentImageContext(); // Do something with the icon here ... UIGraphicsEndImageContext(); 

No longer needed for rect . It seems that the trick uses a negative offset in the crop rectangle, thereby plotting the origin where we want to capture our 50 x 50 image (in this example).

Perhaps there is an easier way. If yes, please weigh!

+7
source share

I wanted to achieve a similar thing, but our answer from the original poster did not quite work. He distorted the image. This is entirely possible only because he did not publish the entire solution and did not change some of the variable initialization parameters:

  (if (size.width > size.height) ratio = px / size.width; 

Wrong for my solution (which wanted to use the maximum possible square from the original image). There is also no need to use UIClipRect - if you make the context the size of the image you want to extract, the actual drawing will not be done outside this rectangle anyway. Itโ€™s just a matter of scaling the size of the image rectangle and shifting one of the origin coordinates. I posted the solution below:

 +(UIImage *)makeIconImage:(UIImage *)image { CGFloat destSize = 400.0; CGRect rect = CGRectMake(0, 0, destSize, destSize); UIGraphicsBeginImageContext(rect.size); if(image.size.width != image.size.height) { CGFloat ratio; CGRect destRect; if (image.size.width > image.size.height) { ratio = destSize / image.size.height; CGFloat destWidth = image.size.width * ratio; CGFloat destX = (destWidth - destSize) / 2.0; destRect = CGRectMake(-destX, 0, destWidth, destSize); } else { ratio = destSize / image.size.width; CGFloat destHeight = image.size.height * ratio; CGFloat destY = (destHeight - destSize) / 2.0; destRect = CGRectMake(0, destY, destSize, destHeight); } [image drawInRect:destRect]; } else { [image drawInRect:rect]; } UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaledImage; } 
+1
source share

Wheeliebin answers are correct, but he forgot the minus sign before destY

 destRect = CGRectMake(0, -destY, destSize, destHeight); 
0
source share

All Articles