iosciimageglkviewcontentmode

How to implement contentMode for GLKView displaying a CIImage?


I have make a GLKView subclass to display CIImage for better performance displaying the output image for a chain of CIFilters.

However, it seem that I need to implement to contentMode logic in order to have a correct inRect for drawImage(image: CIImage, inRect: CGRect, fromRect: CGRect) in drawRect(rect: CGRect) call.

Anyone know how to implement such logic in order to align with the contentMode behaviour of UIImageView`?


Solution

  • For making the contentMode to work in similar way as UIView. I have make the following class.

    final class ContenModeEnforcer {
        static func rectFor(contentMode: UIViewContentMode, fromRect: CGRect, toRect: CGRect) -> CGRect {
            switch contentMode {
            case .scaleToFill:
                return toRect
    
            case .scaleAspectFit:
                return aspectFit(fromRect, toRect: toRect)
    
            case .scaleAspectFill:
                return aspectFill(fromRect, toRect: toRect)
    
            case .redraw:
                return fromRect
    
            case .center:
                let origin = CGPoint(
                    x: (toRect.size.width - fromRect.size.width) / 2,
                    y: (toRect.size.height - fromRect.size.height) / 2)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .top:
                let origin = CGPoint(
                    x: (toRect.size.width - fromRect.size.width) / 2,
                    y: 0.0)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .bottom:
                let origin = CGPoint(
                    x: (toRect.size.width - fromRect.size.width) / 2,
                    y: toRect.size.height - fromRect.size.height)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .left:
                let origin = CGPoint(
                    x: 0.0,
                    y: (toRect.size.height - fromRect.size.height) / 2)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .right:
                let origin = CGPoint(
                    x: toRect.size.width - fromRect.size.width,
                    y: (toRect.size.height - fromRect.size.height) / 2)
                return CGRect(origin: origin, size: fromRect.size)
    
    
            case .topLeft:
                let origin = CGPoint(
                    x: 0.0,
                    y: 0.0)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .topRight:
                let origin = CGPoint(
                    x: toRect.size.width - fromRect.size.width,
                    y: 0.0)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .bottomLeft:
                let origin = CGPoint(
                    x: 0.0,
                    y: toRect.size.height - fromRect.size.height)
                return CGRect(origin: origin, size: fromRect.size)
    
            case .bottomRight:
                let origin = CGPoint(
                    x: toRect.size.width - fromRect.size.width,
                    y: toRect.size.height - fromRect.size.height)
                return CGRect(origin: origin, size: fromRect.size)
            }
        }
    
        static fileprivate func aspectFit(_ fromRect: CGRect, toRect: CGRect) -> CGRect {
            let fromAspectRatio = fromRect.size.width / fromRect.size.height;
            let toAspectRatio = toRect.size.width / toRect.size.height;
    
            var fitRect = toRect
    
            if (fromAspectRatio > toAspectRatio) {
                fitRect.size.height = toRect.size.width / fromAspectRatio;
                fitRect.origin.y += (toRect.size.height - fitRect.size.height) * 0.5;
            } else {
                fitRect.size.width = toRect.size.height  * fromAspectRatio;
                fitRect.origin.x += (toRect.size.width - fitRect.size.width) * 0.5;
            }
    
            return fitRect.integral
        }
    
        static fileprivate func aspectFill(_ fromRect: CGRect, toRect: CGRect) -> CGRect {
            let fromAspectRatio = fromRect.size.width / fromRect.size.height;
            let toAspectRatio = toRect.size.width / toRect.size.height;
    
            var fitRect = toRect
    
            if (fromAspectRatio > toAspectRatio) {
                fitRect.size.width = toRect.size.height  * fromAspectRatio;
                fitRect.origin.x += (toRect.size.width - fitRect.size.width) * 0.5;
            } else {
                fitRect.size.height = toRect.size.width / fromAspectRatio;
                fitRect.origin.y += (toRect.size.height - fitRect.size.height) * 0.5;
            }
    
            return fitRect.integral
        }
    }
    

    In the draw(_:CGRect).

    let inputBounds = image.extent
    let drawableBounds = CGRect(x: 0, y: 0, width: self.drawableWidth, height: self.drawableHeight)
    let targetBounds = ContenModeEnforcer.rectFor(contentMode: contentMode, fromRect: inputBounds, toRect: drawableBounds)
    ciContext.draw(image, in: targetBounds, from: inputBounds)