Правильное использование UIRectClip для масштабирования UIImage до размера иконки - PullRequest
7 голосов
/ 10 сентября 2009

Учитывая UIImage любого размера, я хочу сгенерировать квадратную версию "иконы" размером px пикселей в сторону, без каких-либо искажений (растяжений). Тем не менее, я сталкиваюсь с небольшой загадкой. Не совсем уверен, где проблема. Вот что я делаю до сих пор.

Во-первых, учитывая UImage size, я определяю три вещи: ratio для использования при уменьшении изображения; delta (разница между желаемым размером значка и самой длинной стороной) и 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);
 }

Теперь предположим, что у вас есть изображение шириной 640px и высотой 480px, и мы хотим получить из этого значок размером 50px x 50px. Ширина больше, чем высота, поэтому наши расчеты:

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,5 х 50 х и для работы с ним, а также прямоугольник отсечения, который захватывает «среднюю» часть 50х50.

На домашнем участке! Затем мы устанавливаем наш контекст изображения, рисуем UIImage (называемый здесь myImage) в прямоугольник, устанавливаем прямоугольник отсечения, получаем (предположительно теперь обрезанное) изображение, используем его и, наконец, очищаем наш контекст изображения:

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

 // Do something with the icon here ...

 UIGraphicsEndImageContext();

Только одна проблема: отсечение никогда не происходит! Я получаю изображение шириной 63px и высотой 50px. :(

Возможно, я неправильно использую / неправильно понимаю UIRectClip ? Я пытался перемешать разные вещи: поменять местами rect и clipRect, переместить UIRectClip до drawInRect: . Без кубиков.

Я попытался найти пример этого метода в Интернете, но безрезультатно. Для записи UIRectClip определяется как:

Изменяет текущий путь отсечения пересекая его с указанным прямоугольник.

Перемешивание вещей делает нас немного ближе:

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

Теперь у нас нет искажений, но вырезанное изображение не отцентрировано на оригинале, как я ожидал. Тем не менее, по крайней мере, изображение имеет размер 50x50, хотя имена переменных теперь загрязняются в результате этой перестановки. (Я почтительно оставлю переименование в качестве упражнения для читателя.)

Ответы [ 3 ]

7 голосов
/ 10 сентября 2009

Эврика! Я немного все перепутал. Это работает:

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 (в этом примере).

Возможно, есть более простой способ. Если так, пожалуйста, взвесьте!

1 голос
/ 06 марта 2014

Я хотел добиться подобной вещи, но нашел, что ответ от оригинального плаката не совсем сработал. Это исказило изображение. Вполне возможно, что это связано только с тем, что он не опубликовал все решение и изменил некоторые параметры инициализации переменных:

 (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;
}
0 голосов
/ 15 марта 2014

wheeliebin отвечает правильно, но он забыл знак минус перед судьбой

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