Поворот изображения с помощью 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);
Я рисую изображение в начале координат и поворачиваю его в центре, а затем перемещаю туда, где оно должно быть нарисовано. Не работает.
Ответы
Ответ 1
То, что нужно помнить о преобразованиях координат - ну, одна из вещей, которые нужно запомнить, - это то, что вы не манипулируете объектами, такими как команда Transform в графическом редакторе; вы сами манипулируете пространством.
Представьте, что устройство подвешено на столе рукояткой из гусиной шеи и зафиксировано на месте. Преобразования координат перемещают стол вокруг (перевод) и поворачивают его так или иначе (вращение), и даже сквош и растягивают его (масштабирование).
Еще одна вещь, которую следует помнить о преобразованиях координат, состоит в том, что преобразование всегда относительно начала координат. Представьте, что ваш стол начинается с одного из его углов - соответствует углу вашего вида - прямо под углом, где ваш вид заканчивается на экране. Перевод перемещает угол стола и остальную часть стола с ним, скользнув так или иначе под экраном. Вращение поворачивает стол вокруг этого угла.
Еще одна вещь: Трансформации влияют на будущее, а не на прошлое. Рисование чего-то, а затем попытка его трансформировать не будет работать, потому что вы не преображаете то, что вы нарисовали, вы трансформируете пространство, в которое собираетесь втягиваться. Итак, нам нужно переместить стол, прежде чем мы поместим изображение в нужное место.
(Я упоминаю эту последнюю часть, потому что у вас есть несколько команд преобразования, которые немедленно возвращаются вашим вызовом 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]);
Когда ваш стол так перемещен, центр этого прямоугольника находится прямо под точкой на экране, где вы хотите, чтобы изображение было центрировано, поэтому изображение имеет такую центральную ориентацию.
Ответ 2
Эта функция может рисовать изображение на существующем изображении с углом поворота в радианах.
Swift 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 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)
}
}
Ответ 3
Вы можете использовать следующий метод для поворота изображения.
-(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)
Ответ 4
Если вы в конечном счете рисуете на экране (в отличие от создания изображения для сохранения в файл), вы можете вообще не использовать CGContext, а скорее помещать изображение в CALayer и вращать слой вокруг его опорная точка:
[layer setValue:@(rotationInRadians) forKeyPath:@"transform.rotation.z"];
Если изображение само генерируется (в отличие от статического ресурса из вашего пакета или чего-то, что предоставляется пользователем), вы можете подумать о том, чтобы сделать его из слоев и установить sublayerTransform
своего родительского/общего-предка слоя:
[layerContainingTheConstituentLayers setValue:@(rotationInRadians) forKeyPath:@"sublayerTransform.rotation.z"];
(Могут быть способы сделать это с помощью представлений вместо слоев, я парень Mac, поэтому я не очень глубоко знаю UIView.)