Статьи

Два удобных быстрых расширения: сплайны Эрмита и изменение размера изображения

Как часть моего  редактора Swift Image Tone Curve , я хотел нарисовать плавный сплайн, проходящий через несколько точек. В Swift нет ничего «стандартного», чтобы сделать это — стандартный  UIBezierPath  может рисовать кубические и квадратичные кривые Безье, но требуется немного усилий, чтобы установить контрольные точки, чтобы сделать хорошую непрерывную кривую.

Есть два общих решения:  Catmull Rom  и  Hermite . К счастью, я обнаружил, что  эта замечательная статья  обсуждает реализацию как в Objective-C, так и в более удачной форме, включая  исходный код . Поэтому мне не понадобилось много времени, чтобы перенести этот код в Swift и реализовать его как  расширение  UIBezierPath .

Чтобы использовать мое расширение, просто создайте экземпляр  UIBezierPath , создайте массив  экземпляров CGPoint  — точек, через которые вы хотите, чтобы ваша кривая прошла, — и вызовите  interpolatePointsWithHermite ()  с массивом. Итак, теперь переопределенный  метод drawInContext ()  в  классе ToneCurveEditorCurveLayer моего редактора тоновых кривых  выглядит следующим образом:

override func drawInContext(ctx: CGContext!)
    {
        if let curveValues = toneCurveEditor?.curveValues
        {
            var path = UIBezierPath()
    
            let margin = 20
            let thumbRadius = 15
            let widgetWidth = Int(frame.width)
            let widgetHeight = Int(frame.height) - margin - margin - thumbRadius - thumbRadius

            var interpolationPoints : [CGPoint] = [CGPoint]()
            
            for (i: Int, value: Double) in enumerate(curveValues)
            {
                let pathPointX = i * (widgetWidth / curveValues.count) + (widgetWidth / curveValues.count / 2)
                let pathPointY = thumbRadius + margin + widgetHeight - Int(Double(widgetHeight) * value)
                
                interpolationPoints.append(CGPoint(x: pathPointX,y: pathPointY))
            }
     
            path.interpolatePointsWithHermite(interpolationPoints)
       
            CGContextSetLineJoin(ctx, kCGLineJoinRound)
            CGContextAddPath(ctx, path.CGPath)
            CGContextSetStrokeColorWithColor(ctx, UIColor.blueColor().CGColor)
            CGContextSetLineWidth(ctx, 6)
            CGContextStrokePath(ctx)
        }

    }

Мое  второе полезное расширение  (и то, которое я написал сам на этот раз!) Находится на  UIImage  и изменяет размеры изображений. Мне нужно было сделать это, потому что применение фильтров к огромным изображениям может быть мучительно медленным и пожирать память. При редактировании не требуется применять фильтры к полноразмерному изображению, поэтому изменение размера позволяет моему небольшому приложению использовать прокси.

Это расширение принимает один аргумент, который является стороной ограничивающего квадрата. Возвращенное изображение правильно масштабируется, чтобы поместиться в этом поле

extension UIImage
{
    func resizeToBoundingSquare(#boundingSquareSideLength : CGFloat) -> UIImage
    {
        let imgScale = self.size.width > self.size.height ? boundingSquareSideLength / self.size.width : boundingSquareSideLength / self.size.height
        let newWidth = self.size.width * imgScale
        let newHeight = self.size.height * imgScale
        let newSize = CGSize(width: newWidth, height: newHeight)
        
        UIGraphicsBeginImageContext(newSize)
        
        self.drawInRect(CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        
        UIGraphicsEndImageContext();
        
        return resizedImage
    }

}

… функция возвращает новое изображение и может быть вызвана так:


loadedImage = rawImage.resizeToBoundingSquare (boundingSquareSideLength: 1024)

Это заполняет  загруженный образ  копией  rawImage  с максимальным размером 1024 пикселей.
Оба расширения доступны в моем репозитории GitHub. Расширение  UIBezierPath  является  здесь  и расширение UIImage  находится  здесь .