正确使用 UIRectClip 将 UIImage 缩小到图标大小

发布于 2024-08-04 05:14:41 字数 2345 浏览 3 评论 0原文

给定任何尺寸的 UIImage ,我希望生成一个方形“图标”大小的版本,一侧为 px 像素,没有任何扭曲(拉伸)。然而,我遇到了一个小障碍。不太确定问题出在哪里。这就是我到目前为止正在做的事情。

首先,给定一个 UImage size,我确定三件事:缩小图像时使用的ratiodelta(我们想要的图标大小和最长边之间的差异),和一个offset(用于在剪切图像时计算出我们的原点坐标):

 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);
 }

现在,假设您有一个 640 像素宽 x 480 像素高的图像,我们希望从中获得一个 50 像素 x 50 像素的图标。宽度大于高度,因此我们的计算如下:

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

接下来,我创建一个 CGRect rect,它足够大,可以在不变形的情况下裁剪为我们所需的图标大小,加上用于剪切目的的 clipRect

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

替换上面的值,我们得到:

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}

所以现在我们有一个 62.5px 宽 x 50px 高的矩形可供使用,以及一个抓取“中间”50x50 的剪切矩形部分。

冲刺冲刺!接下来,我们设置图像上下文,将 UIImage (此处称为 myImage)绘制到矩形中,设置剪切矩形,获取(可能是现在剪切的)图像,使用它,最后清理我们的图像上下文:

 UIGraphicsBeginImageContext(rect.size);
 [myImage drawInRect:rect]; 
 UIRectClip(clipRect);
 UIImage *icon = UIGraphicsGetImageFromCurrentImageContext();

 // Do something with the icon here ...

 UIGraphicsEndImageContext();

只有一个问题:永远不会发生剪切!我最终得到了一张 63 像素宽 x 50 像素高的图像。 :(

也许我误用/误解了 UIRectClip?我尝试过调整各种内容:交换 rect的使用 。

我也尝试在网上搜索此方法的示例,但没有成功 UIRectClip 定义为:

修改当前剪切路径 与指定的相交 矩形。

重新排列事物可以让我们更接近一些:

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

现在我们没有失真,但剪切后的图像并不像我预期的那样以原始图像为中心。尽管如此,至少图像是 50x50,尽管变量名称现在由于上述改组而变得混乱。 (我将恭敬地将重命名作为读者的练习。)

Given a UIImage of any dimension, I wish to generate a square "icon" sized version, px pixels to a side, without any distortion (stretching). However, I'm running into a little snag. Not quite sure where the problem is. Here's what I'm doing so far.

First, given a UImage size, I determine three things: the ratio to use when scaling down the image; a delta (the difference between our desired icon size and the longest side), and an offset (which is used to figure out our origin coordinate 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 you have an image 640px wide by 480px high, and we want to get a 50px x 50px icon out of this. The width is greater than the 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 that is large enough to be cropped down to our desired icon size without distortion, plus a clipRect for clipping purposes:

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 from 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 a 62.5px wide by 50px high rect to work with, and a clipping rectangle that grabs the "middle" 50x50 portion.

On to the home stretch! Next, we set up our image context, draw the UIImage (called myImage here) into the rect, set the clipping rectangle, get the (presumably now-clipped) image, use it, and finally clean up our image context:

 UIGraphicsBeginImageContext(rect.size);
 [myImage drawInRect:rect]; 
 UIRectClip(clipRect);
 UIImage *icon = UIGraphicsGetImageFromCurrentImageContext();

 // Do something with the icon here ...

 UIGraphicsEndImageContext();

Only one problem: The clipping never occurs! I end up with an image 63px wide x 50px high. :(

Perhaps I'm misusing/misunderstanding UIRectClip? I've tried shuffling various things around: swapping the use of rect and clipRect, moving UIRectClip before drawInRect:. No dice.

I tried searching for an example of this method online as well, to no avail. For the record, UIRectClip is defined as:

Modifies the current clipping path by
intersecting it with the specified
rectangle.

Shuffling things around gets us a little bit closer:

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

Now we don't have distortion, but the clipped image isn't centered on the original as I expected. Still, at least the image is 50x50, though the variable names are now fouled up as a result of said shuffling. (I'll respectfully leave renaming as an exercise for the reader.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

祁梦 2024-08-11 05:14:41

尤里卡!我的事情有点混乱。这有效:

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();

不再需要 rect。技巧似乎是在剪切矩形中使用偏移量,从而对齐我们想要抓取 50 x 50 图像的原点(在本例中)。

也许有更简单的方法。如果是的话,请权衡一下!

Eureka! I had things a little mixed up. This 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 more need for rect. The trick appears to be using a negative offset in the clipping rectangle, thereby lining up the origin of where we want to grab our 50 x 50 image (in this example).

Perhaps there's an easier way. If so, please weigh in!

如痴如狂 2024-08-11 05:14:41

我想实现类似的目标,但发现原始海报的答案不太有效。它扭曲了图像。这很可能仅仅是因为他没有发布整个解决方案,并且更改了一些变量的初始化方式:

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

对于我的解决方案来说是错误的(它想要使用源图像中尽可能大的正方形)。此外,也没有必要使用 UIClipRect - 如果您将上下文设置为要提取的图像的大小,则无论如何都不会在该矩形之外进行实际绘制。这只是缩放图像矩形的大小并偏移原点坐标之一的问题。我在下面发布了我的解决方案:

+(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;
}

I wanted to achieve a similar thing but found the answer from by the original poster didn't quite work. It distorted the image. This may well be solely because he didn't post the whole solution and had changed some of how the variables are initialised:

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

Was wrong for my solution (which wanted to use the largest possible square from the source image). Also it is not necessary to use UIClipRect - if you make the context the size of the image you want to extract, no actual drawing will be done outside that rect anyway. It is just a matter of scaling the size of the image rect and offsetting one of the origin coordinates. I have posted my 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;
}
堇色安年 2024-08-11 05:14:41

Wheeliebin 的答案是正确的,但他忘记了 destY 前面的减号

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

wheeliebin answers is correct but he forgot a minus sign in front of destY

destRect = CGRectMake(0, -destY, destSize, destHeight);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文