CIBevelSample

A PyObjC Example without documentation

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",
    ]
)