Статьи

Масштабирование, изменение размера и ориентация изображений на ограничивающий квадрат в Swift

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