Ранее в этом году я написал в блоге о небольшом расширении для 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, вот небольшое демонстрационное видео, чтобы подогреть аппетит: