Правильное использование UIRectClip для уменьшения размера UIImage до размера значка

StackOverflow https://stackoverflow.com/questions/1405994

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

 // Do something with the icon here ...

 UIGraphicsEndImageContext();

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

destRect = CGRectMake(0, -destY, destSize, destHeight);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top