# CITransitionSelectorSample

- `Download example`

Image transitions using Core Image.

## Sources

### TransitionSelectorView.py

    import math

    import Cocoa
    import objc
    import Quartz

    TRANSITION_COUNT = 9

    class TransitionSelectorView(Cocoa.NSView):
        _sourceImage = objc.ivar()
        _targetImage = objc.ivar()
        _shadingImage = objc.ivar()
        _blankImage = objc.ivar()
        _maskImage = objc.ivar()
        transitions = objc.ivar()  # Array

        base = objc.ivar(type=objc._C_DBL)

        thumbnailWidth = objc.ivar(type=objc._C_FLT)
        thumbnailHeight = objc.ivar(type=objc._C_FLT)
        thumbnailGap = objc.ivar(type=objc._C_FLT)

        def awakeFromNib(self):
            self.thumbnailWidth = 340.0
            self.thumbnailHeight = 240.0
            self.thumbnailGap = 20.0

            url = Cocoa.NSURL.fileURLWithPath_(
                Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Rose", "jpg")
            )
            self.setSourceImage_(Quartz.CIImage.imageWithContentsOfURL_(url))

            url = Cocoa.NSURL.fileURLWithPath_(
                Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Frog", "jpg")
            )
            self.setTargetImage_(Quartz.CIImage.imageWithContentsOfURL_(url))

            timer = Cocoa.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                1.0 / 30.0, self, "timerFired:", None, True
            )

            self.base = Cocoa.NSDate.pyobjc_classMethods.timeIntervalSinceReferenceDate()
            Cocoa.NSRunLoop.currentRunLoop().addTimer_forMode_(
                timer, Cocoa.NSDefaultRunLoopMode
            )
            Cocoa.NSRunLoop.currentRunLoop().addTimer_forMode_(
                timer, Cocoa.NSEventTrackingRunLoopMode
            )

        def setSourceImage_(self, source):
            self._sourceImage = source

        def setTargetImage_(self, target):
            self._targetImage = target

        def shadingImage(self):
            if self._shadingImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Shading", "tiff")
                )
                self._shadingImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)

            return self._shadingImage

        def blankImage(self):
            if self._blankImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Blank", "jpg")
                )
                self._blankImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)

            return self._blankImage

        def maskImage(self):
            if self._maskImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Mask", "jpg")
                )
                self._maskImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)

            return self._maskImage

        def timerFired_(self, sender):
            self.setNeedsDisplay_(True)

        def imageForTransition_atTime_(self, transitionNumber, t):
            transition = self.transitions[transitionNumber]

            if t % 2.0 < 1.0:
                transition.setValue_forKey_(self._sourceImage, "inputImage")
                transition.setValue_forKey_(self._targetImage, "inputTargetImage")
            else:
                transition.setValue_forKey_(self._targetImage, "inputImage")
                transition.setValue_forKey_(self._sourceImage, "inputTargetImage")

            transition._.inputTime = t % 1.0

            crop = Quartz.CIFilter.filterWithName_("CICrop")
            crop._.inputImage = transition._.outputImage
            crop._.inputRectangle = Quartz.CIVector.vectorWithX_Y_Z_W_(
                0, 0, self.thumbnailWidth, self.thumbnailHeight
            )
            return crop._.outputImage

        def drawRect_(self, rectangle):
            thumbFrame = Quartz.CGRectMake(0, 0, self.thumbnailWidth, self.thumbnailHeight)
            t = 0.4 * (
                Cocoa.NSDate.pyobjc_classMethods.timeIntervalSinceReferenceDate()
                - self.base
            )

            context = Cocoa.NSGraphicsContext.currentContext().CIContext()

            if self.transitions is None:
                self.transitions = [None] * TRANSITION_COUNT
                self.setupTransitions()

            w = int(math.ceil(math.sqrt(TRANSITION_COUNT)))
            origin = Quartz.CGPoint()

            for i in range(TRANSITION_COUNT):
                origin.x = (i % w) * (self.thumbnailWidth + self.thumbnailGap)
                origin.y = (i / w) * (self.thumbnailHeight + self.thumbnailGap)

                if context is not None:
                    context.drawImage_atPoint_fromRect_(
                        self.imageForTransition_atTime_(i, t + 0.1 * i), origin, thumbFrame
                    )

        def setupTransitions(self):
            w = self.thumbnailWidth
            h = self.thumbnailHeight

            extent = Quartz.CIVector.vectorWithX_Y_Z_W_(0, 0, w, h)

            self.transitions[0] = Quartz.CIFilter.filterWithName_("CISwipeTransition")
            self.transitions[0]._.inputExtent = extent
            self.transitions[
                0
            ]._.inputColor = Quartz.CIColor.colorWithRed_green_blue_alpha_(0, 0, 0, 0)
            self.transitions[0]._.inputAngle = 0.3 * math.pi
            self.transitions[0]._.inputWidth = 80.0
            self.transitions[0]._.inputOpacity = 0.0

            self.transitions[1] = Quartz.CIFilter.filterWithName_("CIDissolveTransition")

            self.transitions[2] = Quartz.CIFilter.filterWithName_(
                "CISwipeTransition"
            )  # dupe
            self.transitions[2]._.inputExtent = extent
            self.transitions[
                2
            ]._.inputColor = Quartz.CIColor.colorWithRed_green_blue_alpha_(0, 0, 0, 0)
            self.transitions[2]._.inputAngle = math.pi
            self.transitions[2]._.inputWidth = 2.0
            self.transitions[2]._.inputOpacity = 0.2

            self.transitions[3] = Quartz.CIFilter.filterWithName_("CIModTransition")
            self.transitions[3]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.5 * w, 0.5 * h
            )
            self.transitions[3]._.inputAngle = math.pi * 0.1
            self.transitions[3]._.inputRadius = 30.0
            self.transitions[3]._.inputCompression = 10.0

            self.transitions[4] = Quartz.CIFilter.filterWithName_("CIFlashTransition")
            self.transitions[4]._.inputExtent = extent
            self.transitions[4]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.3 * w, 0.7 * h
            )
            self.transitions[
                4
            ]._.inputColor = Quartz.CIColor.colorWithRed_green_blue_alpha_(1, 0.8, 0.6, 1)
            self.transitions[4]._.inputMaxStriationRadius = 2.5
            self.transitions[4]._.inputStriationStrength = 0.5
            self.transitions[4]._.inputStriationContrast = 1.37
            self.transitions[4]._.inputFadeThreshold = 0.85

            self.transitions[5] = Quartz.CIFilter.filterWithName_(
                "CIDisintegrateWithMaskTransition"
            )
            self.transitions[5]._.inputMaskImage = self.maskImage()
            self.transitions[5]._.inputShadowRadius = 10.0
            self.transitions[5]._.inputShadowDensity = 0.7
            self.transitions[5]._.inputShadowOffset = Quartz.CIVector.vectorWithX_Y_(
                0.0, -0.05 * h
            )

            self.transitions[6] = Quartz.CIFilter.filterWithName_("CIRippleTransition")
            self.transitions[6]._.inputExtent = extent
            self.transitions[6]._.inputShadingImage = self.shadingImage()
            self.transitions[6]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.5 * w, 0.5 * h
            )
            self.transitions[6]._.inputWidth = 80.0
            self.transitions[6]._.inputScale = 30.0

            self.transitions[7] = Quartz.CIFilter.filterWithName_("CICopyMachineTransition")
            self.transitions[7]._.inputExtent = extent
            self.transitions[
                7
            ]._.inputColor = Quartz.CIColor.colorWithRed_green_blue_alpha_(0.6, 1, 0.8, 1)
            self.transitions[7]._.inputAngle = 0
            self.transitions[7]._.inputWidth = 40
            self.transitions[7]._.inputOpacity = 1.0

            self.transitions[8] = Quartz.CIFilter.filterWithName_("CIPageCurlTransition")
            self.transitions[8]._.inputExtent = extent
            self.transitions[8]._.inputShadingImage = self.shadingImage()
            self.transitions[8]._.inputBacksideImage = self.blankImage()
            self.transitions[8]._.inputAngle = -0.2 * math.pi
            self.transitions[8]._.inputRadius = 70.0

### main.py

    import TransitionSelectorView  # noqa: F401
    from PyObjCTools import AppHelper

    AppHelper.runEventLoop()

### setup.py

    """
    Script for building the example.

    Usage:
        python3 setup.py py2app
    """
    from setuptools import setup

    setup(
        name="CITransitionSelectorSample",
        app=["main.py"],
        data_files=[
            "English.lproj",
            "Blank.jpg",
            "Frog.jpg",
            "Mask.jpg",
            "Rose.jpg",
            "Shading.tiff",
        ],
        setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
    )