How to implement contentMode for GLKView displaying a CIImage?

588 views Asked by At

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`?

2

There are 2 answers

0
Morty Choi On BEST ANSWER

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)
0
Will Loew-Blosser On

In iOS 10.3 the setting of the contentMode does not seem to affect the glkView. It looks like it is always a bottomLeft mode.

To make the glkView display a CIImage in format that is viewable this is what worked for me.

The rect that is passed in the glkView(_ view: GLKView, drawIn rect: CGRect) needs to be modified by the view contentScaleFactor

  1. This code assumes you have initialized the EAGLContext in the viewDidLoad method of the GLKViewController (my glkView is named effectView2)

    context = EAGLContext.init(api: EAGLRenderingAPI.openGLES2)
    if context != nil {
    effectView2.context = self.context!
    
    EAGLContext.setCurrent(self.context)
    ciContext = CIContext.init(eaglContext: context!)
    
  2. set a scale factor after the glkView is set

    myScaleFactor = view.contentScaleFactor

  3. in your glkView(_ view: GLKView, drawIn rect: CGRect) do this

    let adjustedRect = CGRect(x: 0.0, y: 0.0, width:  rect.width * myScaleFactor, height:  rect.height * myScaleFactor )

Then with your ciContext draw the ciImage in the adjustedRect. Use the extent of the ciImage as the from parm like this:

ciContext!.draw(outputImage, in: adjustedRect , from: (ciSourceImage?.extent)!)
  1. Oh yes. I assume you have found the tech note for adjusting the GLKView scale factor. It calls for a line in your subclass of GLKView. See https://developer.apple.com/library/content/qa/qa1909/_index.html

Most likely you will need some tweaks in this approach. I'm currently working on the issue orientation changes for different source ciImages.

It's taken me WAY too long to find this much so I thought to share these findings.. Please post any improvements that you find !

Hope it helps Will L-B