Если я правильно понял ваш вопрос:
- У вас есть представление изображения, содержащее изображение, которое могло быть уменьшено (или даже увеличено) с помощью
UIViewContentModeScaleAspectFit
.
- У вас есть путь Безье, точки которого находятся в геометрии (системе координат) этого вида изображения.
И теперь вы хотите создать копию изображения в исходном разрешении, замаскированную путем Безье.
Мы можем представить себе изображение как имеющее собственную геометрию, с началом в верхнем левом углу изображения и одной единицей по каждой оси, равной одной точке. Итак, что нам нужно сделать, это:
- Создайте графический рендерер, достаточно большой, чтобы отобразить изображение без масштабирования. Геометрия этого средства визуализации является геометрией изображения.
- Преобразуйте путь Безье из геометрии представления в геометрию средства визуализации.
- Примените преобразованный путь к области отсечения средства визуализации.
- Нарисуйте изображение (непреобразованное) в средстве визуализации.
Шаг 2 — сложный, потому что нам нужно придумать правильное CGAffineTransform
. В сценарии подгонки аспекта преобразование должно не только масштабировать изображение, но, возможно, перемещать его либо по оси x, либо по оси y (но не по обеим). Но давайте будем более общими и поддержим другие настройки UIViewContentMode
. Вот категория, которая позволяет вам запросить UIImageView
для преобразования, которое преобразует точки в геометрии вида в точки в геометрии изображения:
@implementation UIImageView (ImageGeometry)
/**
* Return a transform that converts points in my geometry to points in the
* image's geometry. The origin of the image's geometry is at its upper
* left corner, and one unit along each axis is one point in the image.
*/
- (CGAffineTransform)imageGeometryTransform {
CGRect viewBounds = self.bounds;
CGSize viewSize = viewBounds.size;
CGSize imageSize = self.image.size;
CGFloat xScale = imageSize.width / viewSize.width;
CGFloat yScale = imageSize.height / viewSize.height;
CGFloat tx, ty;
switch (self.contentMode) {
case UIViewContentModeScaleToFill: tx = 0; ty = 0; break;
case UIViewContentModeScaleAspectFit:
if (xScale > yScale) { tx = 0; ty = 0.5; yScale = xScale; }
else if (xScale < yScale) { tx = 0.5; ty = 0; xScale = yScale; }
else { tx = 0; ty = 0; }
break;
case UIViewContentModeScaleAspectFill:
if (xScale < yScale) { tx = 0; ty = 0.5; yScale = xScale; }
else if (xScale > yScale) { tx = 0.5; ty = 0; xScale = yScale; }
else { tx = 0; ty = 0; imageSize = viewSize; }
break;
case UIViewContentModeCenter: tx = 0.5; ty = 0.5; xScale = yScale = 1; break;
case UIViewContentModeTop: tx = 0.5; ty = 0; xScale = yScale = 1; break;
case UIViewContentModeBottom: tx = 0.5; ty = 1; xScale = yScale = 1; break;
case UIViewContentModeLeft: tx = 0; ty = 0.5; xScale = yScale = 1; break;
case UIViewContentModeRight: tx = 1; ty = 0.5; xScale = yScale = 1; break;
case UIViewContentModeTopLeft: tx = 0; ty = 0; xScale = yScale = 1; break;
case UIViewContentModeTopRight: tx = 1; ty = 0; xScale = yScale = 1; break;
case UIViewContentModeBottomLeft: tx = 0; ty = 1; xScale = yScale = 1; break;
case UIViewContentModeBottomRight: tx = 1; ty = 1; xScale = yScale = 1; break;
default: return CGAffineTransformIdentity; // Mode not supported by UIImageView.
}
tx *= (imageSize.width - xScale * (viewBounds.origin.x + viewSize.width));
ty *= (imageSize.height - yScale * (viewBounds.origin.y + viewSize.height));
CGAffineTransform transform = CGAffineTransformMakeTranslation(tx, ty);
transform = CGAffineTransformScale(transform, xScale, yScale);
return transform;
}
@end
Вооружившись этим, мы можем написать код, маскирующий изображение. В моем тестовом приложении у меня есть подкласс UIImageView
с именем PathEditingView
, который обрабатывает редактирование пути Безье. Итак, мой контроллер представления создает замаскированное изображение следующим образом:
- (UIImage *)maskedImage {
UIImage *image = self.pathEditingView.image;
UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
format.scale = image.scale;
format.prefersExtendedRange = image.imageRendererFormat.prefersExtendedRange;
format.opaque = NO;
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:image.size format:format];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
UIBezierPath *path = [self.pathEditingView.path copy];
[path applyTransform:self.pathEditingView.imageGeometryTransform];
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextAddPath(gc, path.CGPath);
CGContextClip(gc);
[image drawAtPoint:CGPointZero];
}];
}
И это выглядит так:
![демонстрация маскировки](https://i.stack.imgur.com/7wRNu.gif)
Конечно, трудно сказать, что выходное изображение имеет полное разрешение. Давайте исправим это, обрезав выходное изображение до ограничивающей рамки пути Безье:
- (UIImage *)maskedAndCroppedImage {
UIImage *image = self.pathEditingView.image;
UIBezierPath *path = [self.pathEditingView.path copy];
[path applyTransform:self.pathEditingView.imageGeometryTransform];
CGRect pathBounds = CGPathGetPathBoundingBox(path.CGPath);
UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
format.scale = image.scale;
format.prefersExtendedRange = image.imageRendererFormat.prefersExtendedRange;
format.opaque = NO;
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:pathBounds.size format:format];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(gc, -pathBounds.origin.x, -pathBounds.origin.y);
CGContextAddPath(gc, path.CGPath);
CGContextClip(gc);
[image drawAtPoint:CGPointZero];
}];
}
Маскирование и обрезка вместе выглядят так:
![демонстрация маскирования и кадрирования](https://i.stack.imgur.com/VEFkO.gif)
Вы можете видеть в этой демонстрации, что выходное изображение имеет гораздо больше деталей, чем было видно во входном представлении, потому что оно было создано с полным разрешением входного изображения.
person
rob mayoff
schedule
11.02.2018