CIBevelSample

Translation into python of and ADC example of the same name.

Sources

CIBevelView.py

import objc
from objc import super
import Quartz

from SampleCIView import SampleCIView

from math import sin

import objc

NUM_POINTS=4

class CIBevelView (SampleCIView):
    currentPoint        = objc.ivar(type=objc._C_INT)
    points              = objc.ivar()
    angleTime           = objc.ivar(type=objc._C_FLT)
    lineImage           = objc.ivar()
    twirlFilter         = objc.ivar()
    heightFieldFilter   = objc.ivar()
    shadedFilter        = objc.ivar()


    def initWithFrame_(self, frameRect):
        self = super(CIBevelView, self).initWithFrame_(frameRect)
        if self is None:
            return None

        self.points = [ None ] * NUM_POINTS
        self.points[0] = Quartz.CGPointMake(0.5 * frameRect.size.width, frameRect.size.height - 100.0)
        self.points[1] = Quartz.CGPointMake(150.0, 100.0)
        self.points[2] = Quartz.CGPointMake(frameRect.size.width - 150.0, 100.0)
        self.points[3] = Quartz.CGPointMake(0.7*self.points[0].x + 0.3*self.points[2].x, 0.7*self.points[0].y + 0.3*self.points[2].y)

        url = Cocoa.NSURL.fileURLWithPath_(
           Cocoa.NSBundle.mainBundle().pathForResource_ofType_("lightball", "tiff"))

        self.lightball = Quartz.CIImage.imageWithContentsOfURL_(url)

        self.heightFieldFilter = Quartz.CIFilter.filterWithName_("CIHeightFieldFromMask")
        self.heightFieldFilter.setDefaults()
        self.heightFieldFilter.setValue_forKey_(15.0, "inputRadius")

        self.twirlFilter = Quartz.CIFilter.filterWithName_("CITwirlDistortion")
        self.twirlFilter.setDefaults()
        self.twirlFilter.setValue_forKey_(
            Quartz.CIVector.vectorWithX_Y_(
                0.5*frameRect.size.width,
                0.5*frameRect.size.height),
            "inputCenter")
        self.twirlFilter.setValue_forKey_(300.0, "inputRadius")
        self.twirlFilter.setValue_forKey_(0.0, "inputAngle")

        self.shadedFilter = Quartz.CIFilter.filterWithName_("CIShadedMaterial")
        self.shadedFilter.setDefaults()
        self.shadedFilter.setValue_forKey_(self.lightball, "inputShadingImage")
        self.shadedFilter.setValue_forKey_(20.0, "inputScale")

        # 1/30 second should give us decent animation
        Cocoa.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                1.0/30.0, self, 'changeTwirlAngle:', None, True)
        return self


    def changeTwirlAngle_(self, timer):
        self.angleTime += timer.timeInterval()
        self.twirlFilter.setValue_forKey_(
                -0.2 * sin(self.angleTime*5.0), 'inputAngle')
        self.updateImage()

    def mouseDragged_(self, event):
        loc = self.convertPoint_fromView_(event.locationInWindow(), None)
        self.points[self.currentPoint].x = loc.x
        self.points[self.currentPoint].y = loc.y
        self.lineImage = None

        # normally we'd want this, but the timer will cause us to
        # redisplay anyway
        #self.setNeedsDisplay_(True)

    def mouseDown_(self, event):
        d   = 1e4
        loc = self.convertPoint_fromView_(event.locationInWindow(), None)
        for i in range(NUM_POINTS):
            x = self.points[i].x - loc.x
            y = self.points[i].y - loc.y
            t = x*x + y*y

            if t < d:
                self.currentPoint = i
                d = t

        self.mouseDragged_(event)

    def updateImage(self):
        context = Cocoa.NSGraphicsContext.currentContext().CIContext()
        if self.lineImage is None:
            bounds  = self.bounds()
            layer   = context.createCGLayerWithSize_info_(
                    Quartz.CGSizeMake(Cocoa.NSWidth(bounds), Cocoa.NSHeight(bounds)), None)

            cg      = Quartz.CGLayerGetContext(layer)

            Quartz.CGContextSetRGBStrokeColor(cg, 1,1,1,1)
            Quartz.CGContextSetLineCap(cg, Quartz.kCGLineCapRound)

            Quartz.CGContextSetLineWidth(cg, 60.0)
            Quartz.CGContextMoveToPoint(cg, self.points[0].x, self.points[0].y)
            for i in range(1, NUM_POINTS):
                Quartz.CGContextAddLineToPoint(cg, self.points[i].x, self.points[i].y)
            Quartz.CGContextStrokePath(cg)

            self.lineImage = CIImage.alloc().initWithCGLayer_(layer)

        self.heightFieldFilter.setValue_forKey_(self.lineImage, "inputImage")
        self.twirlFilter.setValue_forKey_(
                self.heightFieldFilter.valueForKey_("outputImage"),
                "inputImage")

        self.shadedFilter.setValue_forKey_(
                self.twirlFilter.valueForKey_("outputImage"),
                "inputImage")

        self.setImage_(self.shadedFilter.valueForKey_("outputImage"))

SampleCIView.py

"""
SampleCIView - simple OpenGL based CoreImage view
"""
import objc
import Cocoa
import Quartz
import CGL

from OpenGL.GL import *

# XXX: this may or may not be a bug in the OpenGL bindings
from OpenGL.GL.APPLE.transform_hint import *

import objc

# The default pixel format
_pf = None

class SampleCIView (Cocoa.NSOpenGLView):
    _context = objc.ivar()
    _image = objc.ivar()
    _lastBounds = objc.ivar(type=Cocoa.NSRect.__typestr__)

    @classmethod
    def defaultPixelFormat(self):
        global _pf

        if _pf is None:
            # Making sure the context's pixel format doesn't have a recovery
            # renderer is important - otherwise CoreImage may not be able to
            # create deeper context's that share textures with this one.

            attr = ( Cocoa.NSOpenGLPFAAccelerated,
                    Cocoa.NSOpenGLPFANoRecovery, Cocoa.NSOpenGLPFAColorSize, 32 )
            _pf = Cocoa.NSOpenGLPixelFormat.alloc().initWithAttributes_(attr)

        return _pf

    def image(self):
        return self._image

    def setImage_dirtyRect_(self, image, r):
        if self._image is not image:
            self._image = image

            if Quartz.CGRectIsInfinite(r):
                self.setNeedsDisplay_(True)
            else:
                self.setNeedsDisplayInRect_(r)

    def setImage_(self, image):
        self.setImage_dirtyRect_(image, CGRectInfinite)

    def prepareOpenGL(self):
        parm = 1

        # Enable beam-synced updates.

        self.openGLContext().setValues_forParameter_(
                (parm,), Cocoa.NSOpenGLCPSwapInterval)

        # Make sure that everything we don't need is disabled. Some of these
        # are enabled by default and can slow down rendering.

        glDisable(GL_ALPHA_TEST)
        glDisable(GL_DEPTH_TEST)
        glDisable(GL_SCISSOR_TEST)
        glDisable(GL_BLEND)
        glDisable(GL_DITHER)
        glDisable(GL_CULL_FACE)
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        glDepthMask(GL_FALSE)
        glStencilMask(0)
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glHint (GL_TRANSFORM_HINT_APPLE, GL_FASTEST)

    def viewBoundsDidChange_(self, bounds):
        # For subclasses.
        pass

    def updateMatrices(self):
        r = self.bounds()

        if r != self._lastBounds:
            self.openGLContext().update()

            # Install an orthographic projection matrix (no perspective)
            # with the origin in the bottom left and one unit equal to one
            # device pixel.

            glViewport(0, 0, r.size.width, r.size.height)

            glMatrixMode(GL_PROJECTION)
            glLoadIdentity()
            glOrtho(0, r.size.width, 0, r.size.height, -1, 1)

            glMatrixMode(GL_MODELVIEW)
            glLoadIdentity()

            self._lastBounds = r

            self.viewBoundsDidChange_(r)

    def drawRect_(self, r):
        self.openGLContext().makeCurrentContext()

        # Allocate a CoreImage rendering context using the view's OpenGL
        # context as its destination if none already exists.

        if self._context is None:
            pf = self.pixelFormat()
            if pf is None:
                pf = type(self).defaultPixelFormat()

            self._context=Quartz.CIContext.contextWithCGLContext_pixelFormat_options_(
                CGL.CGLGetCurrentContext(), pf.CGLPixelFormatObj(), None)

        ir = Cocoa.CGRectIntegral(r)

        if Cocoa.NSGraphicsContext.currentContextDrawingToScreen():
            self.updateMatrices()

            # Clear the specified subrect of the OpenGL surface then
            # render the image into the view. Use the GL scissor test to
            # clip to * the subrect. Ask CoreImage to generate an extra
            # pixel in case * it has to interpolate (allow for hardware
            # inaccuracies)

            rr = Cocoa.CGRectIntersection (Cocoa.CGRectInset(ir, -1.0, -1.0),
                        self._lastBounds)

            glScissor(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height)
            glEnable(GL_SCISSOR_TEST)

            glClear(GL_COLOR_BUFFER_BIT)

            if self.respondsToSelector_('drawRect:inCIContext:'):
                self.drawRect_inCIContext_(rr, self._context)

            elif self._image is not None:
                self._context.drawImage_atPoint_fromRect_(
                    self._image, rr.origin, rr)

            glDisable(GL_SCISSOR_TEST)

            # Flush the OpenGL command stream. If the view is double
            # buffered this should be replaced by [[self openGLContext]
            # flushBuffer].

            glFlush ()

        else:
            # Printing the view contents. Render using CG, not OpenGL.

            if self.respondsToSelector_('drawRect:inCIContext:'):
                self.drawRect_inCIContext_(ir, self._context)

            elif self._image is not None:
                cgImage = self._context.createCGImage_fromRect_(
                    self._image, ir)

                if cgImage is not None:
                    Quartz.CGContextDrawImage(
                            Cocoa.NSGraphicsContext.currentContext().graphicsPort(),
                            ir, cgImage)

main.py

from PyObjCTools import AppHelper

import CIBevelView
import SampleCIView

import objc; objc.setVerbose(True)

AppHelper.runEventLoop()

setup.py

"""
Script for building the example.

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

setup(
    name="CIBevelSample",
    app=["main.py"],
    data_files=[
        "English.lproj",
        "lightball.tiff"
    ]
    setup_requires=[
        "py2app",
        "pyobjc-framework-Cocoa",
        "pyobjc-framework-Quartz",
    ]
)

Resources