Повернуть изображение с помощью CGContextDrawImage
Как я могу повернуть изображение, нарисованное CGContextDrawImage()
в его центре?
В drawRect:
CGContextSaveGState(c);
rect = CGRectOffset(rect, -rect.size.width / 2, -rect.size.height / 2);
CGContextRotateCTM(c, angle);
CGContextDrawImage(c, rect, doodad.CGImage);
CGContextRotateCTM(c, -angle);
CGContextTranslateCTM(c, pt.x, pt.y);
prevRect = rect;
CGContextRestoreGState(c);
Я рисую изображение в начале координат и поворачиваю его в центре, а затем перемещаю туда, где оно должно быть нарисовано. Не работает.
4 ответа
Что нужно помнить о преобразованиях координат - хорошо, одна из вещей, о которых следует помнить - это то, что вы не манипулируете объектами, как команда Transform в графическом редакторе; Вы манипулируете самим пространством.
Представьте, что устройство подвешено к столу с помощью гусиной шеи и зафиксировано на месте. Преобразования координат перемещают стол (перевод), и поворачивают его так или иначе (вращение), и даже раздавливают и растягивают (масштабирование).
Еще одна вещь, которую следует помнить о преобразованиях координат, заключается в том, что преобразование всегда связано с источником. Представьте, что ваш стол начинается с одного из его углов, соответствующих углу вашего обзора, прямо под углом того места, где ваш вид заканчивается на экране. Translating перемещает угол стола, а остальную часть стола с ним, так или иначе сдвигая его под экран. Вращение поворачивает стол за этот угол.
Еще одна вещь: преобразования влияют на будущее, а не на прошлое. Рисование чего-то, а затем попытка трансформировать это не сработает, потому что вы не трансформируете то, что нарисовали, вы трансформируете пространство, в которое собираетесь рисовать. Итак, нам нужно переместить стол, прежде чем мы сможем разместить изображение в нужном месте.
(Я упоминаю эту последнюю часть, потому что у вас есть пара команд преобразования, которые немедленно возвращаются вашим CGContextRestoreGState
вызов. Там нет рисунка между ними, чтобы они могли повлиять.)
Итак, начнем с необращенного прямоугольника изображения.
CGRect imageRect = { pointWhereYouWantTheImageCentered, doodad.size };
Сейчас, CGContextDrawImage
принимает прямоугольник, но, как вы знаете, он будет рисовать изображение из левого нижнего угла этого прямоугольника, а не из центра. (Отсюда и все упражнение.)
Итак, вот что вы собираетесь делать. В конце этого вы нарисуете изображение не в той точке, показанной выше, а в нулевой точке, то есть в самом углу стола. (Не по центру в углу, но с углом изображения на углу стола.)
Как это будет работать? Требуется некоторая настройка. Тебе придется переместить свой стол.
Во-первых, вам нужно переместить ваш стол вверх и вправо, пока исходный угол вашего стола не окажется в желаемой центральной точке:
CGContextTranslateCTM(context, imageRect.origin.x, imageRect.origin.y);
Затем вы делаете вращение (поворачивая стол за исходный угол):
CGContextRotateCTM(context, angleInRadians);
Затем вы перемещаете стол обратно вниз и влево на половину ширины и высоты изображения:
CGContextTranslateCTM(context, imageRect.size.width * -0.5, imageRect.size.height * -0.5);
И, наконец, поместите изображение в угол стола.
CGContextDrawImage(context, (CGRect){ CGPointZero, imageRect.size }, [doodad CGImage]);
Если ваш стол перемещен, центр этого прямоугольника находится непосредственно под точкой на экране, где вы хотите, чтобы изображение было в центре, поэтому изображение будет таким же центром.
Эта функция может нарисовать изображение на существующем изображении с углом поворота в радианах.
Свифт 3:
extension UIImage {
func putImage(image: UIImage, on rect: CGRect, angle: CGFloat = 0.0) -> UIImage{
let drawRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(drawRect.size, false, 1.0)
// Start drawing self
self.draw(in: drawRect)
// Drawing new image on top
let context = UIGraphicsGetCurrentContext()!
// Get the center of new image
let center = CGPoint(x: rect.midX, y: rect.midY)
// Set center of image as context action point, so rotation works right
context.translateBy(x: center.x, y: center.y)
context.saveGState()
// Rotate the context
context.rotate(by: angle)
// Context origin is image's center. So should draw image on point on origin
image.draw(in: CGRect(origin: CGPoint(x: -rect.size.width/2, y: -rect.size.height/2), size: rect.size), blendMode: .normal, alpha:
1.0)
// Go back to context original state.
context.restoreGState()
// Get new image
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
Если вам нужно создать и очистить изображение с размером и цветом, используйте:
extension UIImage {
convenience init?(size: CGSize, color: UIColor) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, true, 1.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}
Если вы в конечном итоге рисуете на экран (в отличие от генерации изображения для сохранения в файл), вы можете вообще не использовать CGContext, а поместить изображение в CALayer и повернуть слой вокруг его точки привязки:
[layer setValue:@(rotationInRadians) forKeyPath:@"transform.rotation.z"];
Если изображение само генерируется (в отличие от статического ресурса из вашего пакета или чего-то, предоставленного пользователем), вы можете рассмотреть возможность сделать его из слоев и установить sublayerTransform
их родительского / общего предка слоя:
[layerContainingTheConstituentLayers setValue:@(rotationInRadians) forKeyPath:@"sublayerTransform.rotation.z"];
(Могут быть способы сделать это с представлениями, а не со слоями; я парень из Mac, поэтому я не очень хорошо знаю UIView.)
Вы можете использовать следующий метод для поворота изображения.
-(UIImage*)rotateImage:(UIImage*)img forAngle:(CGFloat)radian {
UIView *rotatedViewBox = [[UIView alloc]initWithFrame:CGRectMake(0, 0, img.size.width, img.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(radian);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, rotatedSize.width, rotatedSize.height);
CGContextRotateCTM(context, radian);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-img.size.width, -img.size.height, img.size.width, img.size.height), img.CGImage);
UIImage *returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImg;
}
Если вы используете угол, то вот макрос для преобразования градусов в радиан
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)