Правильное использование UIRectClip для уменьшения размера UIImage до размера значка
Вопрос
Учитывая, что 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);