Ранее в этом году я написал в блоге о небольшом расширении для UIImage для изменения размера изображений в пределах ограничительного квадрата . Часть моего текущего проекта, переписывание моего приложения Nodality в Swift, требует аналогичной функциональности, но я хотел улучшить качество масштабирования и обрабатывать изображения с различной ориентацией (например, портретная и альбомная ориентация).
Моя функция r esizeToBoundingSquare () принимает изображение и числовое значение, которое определяет ширину ограничивающего квадрата, и возвращает UIImage . Таким образом, подпись:
func resizeToBoundingSquare(sourceImage: UIImage, #boundingSquareSideLength : CGFloat) -> UIImage
Моим первым шагом было использование передискретизации Lanczos, а не просто использование drawInRect () в новом размере. Lanczos предлагает плавную передискретизацию, которая должна привести к улучшению качества изображения, и он доступен как один из встроенных фильтров Core Image.
Код для выполнения фильтра и генерации CIImage правильного размера :
let imgScale = sourceImage.size.width < sourceImage.size.height ? boundingSquareSideLength / sourceImage.size.width : boundingSquareSideLength / sourceImage.size.height
let scaleTransform = CIFilter(name: "CILanczosScaleTransform")
scaleTransform.setValue(CIImage(image: sourceImage), forKey: "inputImage")
scaleTransform.setValue(imgScale, forKey: "inputScale")
scaleTransform.setValue(1.0, forKey: "inputAspectRatio")
let outputImage = scaleTransform.valueForKey("outputImage") as CIImage
CIImage масштабируется , но не квадрат. Чтобы создать обрезанный и центрированный UIImage, я использую следующий код:
let context = CIContext(options: nil)
let extent = outputImage.extent()
let xOffset = Int(extent.width) > NodeConstants.imageWidth ? (extent.width - boundingSquareSideLength) / 2 : 0
let yOffset = Int(extent.height) > NodeConstants.imageWidth ? (extent.height - boundingSquareSideLength) / 2 : 0
let scaledImage = UIImage(CGImage: context.createCGImage(outputImage, fromRect: CGRect(x: xOffset, y: yOffset, width: boundingSquareSideLength, height: boundingSquareSideLength)))!
Теперь у нас есть масштабированный квадратный UIImage, но есть одна последняя проблема. Если исходный источник был снят в альбомной ориентации, изображение поворачивается. Итак, заключительные шаги — посмотреть на ориентацию исходного изображения и соответственно повернуть его. Сначала я определяю угол поворота:
var angle = 0.0;
if (sourceImage.imageOrientation == UIImageOrientation.Right)
{
angle = 90.0
}
else if (sourceImage.imageOrientation == UIImageOrientation.Left)
{
angle = -90.0
}
else if (sourceImage.imageOrientation == UIImageOrientation.Down)
{
angle = 180
}
else if (sourceImage.imageOrientation == UIImageOrientation.Up)
{
angle = 0.0
}
Затем я использую CGContext, чтобы нарисовать изображение. Поскольку точка поворота вращения находится в точке (0,0), мне нужно перевести контекст на половину ширины ограничивающего квадрата перед поворотом и выполнить drawAtPoint () в противоположность этому переводу:
UIGraphicsBeginImageContext(CGSize(width: boundingSquareSideLength, height: boundingSquareSideLength))
let cgContext = UIGraphicsGetCurrentContext()
let offset = boundingSquareSideLength / 2.0
CGContextTranslateCTM(cgContext, offset, offset)
CGContextRotateCTM(cgContext, CGFloat(angle * M_PI / 180))
scaledImage.drawAtPoint(CGPoint(x: -offset, y: -offset))
Наконец, я получаю UIImage из контекста и освобождаю его:
let final = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return final
И в результате получаются прекрасно масштабированные изображения, центрированные и обрезанные до квадрата!
Этот исходный код не является общедоступным на GitHub , но этот пост содержит полный исходный код resizeToBoundingSquare ().
Наконец, если вам интересно, как Nodality выглядит в Swift, вот небольшое демонстрационное видео, чтобы подогреть аппетит: