Racket [Scheme] / Cocoa Glue

I’ve been working on some video apps in Racket on my Mac, but I want to use some of the OSX features like Core Image Filters and QTKit. Racket has really good foreign function interface to both C and Objective-C, but a few things fall through the cracks. For example, it does not seem possible to pass a structure on the stack from Racket into Objective-C, which is required for a number of the image and video related APIs. Some other things are just easier to write in Objective-C. So I’ve been working on a library that makes some of these things easier.

My library consists of three pieces. An Objective-C library with a C interface, a Racket FFI interface to that library, and some Racket wrappers around Objective-C to make the interface simpler. Here’s some pieces.

Below is the Objective-C code for the C Interface for some image conversion routines. The most useful piece of this is the conversion routines from CIImage to NSImage and back, which are useful when using the Core Image Filters.

extern int glue_image_width( NSImage * img )
{
   return [img size].width;
}

extern int glue_image_height( NSImage * img )
{
   return [img size].height;
}

extern GLuint glue_NSBitmapImageRep_to_texture( NSBitmapImageRep * rep )
{  
   GLuint targetTexture;
   
   glGenTextures( 1, &targetTexture );
   int width            = [rep pixelsWide];
   int height           = [rep pixelsHigh];
   GLenum format        = [rep hasAlpha] ? GL_RGBA : GL_RGB;
   int numPixelsInRow   = [rep bytesPerRow] / ([rep bitsPerPixel] >> 3);
   
   GLenum target = GL_TEXTURE_RECTANGLE_EXT;
   glBindTexture( target, targetTexture );
   
   glPixelStorei(GL_UNPACK_ROW_LENGTH, numPixelsInRow); 
   
   glTexImage2D( target, 0, GL_RGBA, width, height, 0, format, GL_UNSIGNED_BYTE, [rep bitmapData] );
   glTexParameterf( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
   glTexParameterf( target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
   
   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
   return targetTexture;
}

extern NSBitmapImageRep * glue_NSImage_to_NSBitmapImageRep( NSImage * img )
{
   return [NSBitmapImageRep imageRepWithData: [img TIFFRepresentation]];
}

extern CIImage * glue_NSImage_to_CIImage( NSImage * ns )
{
   NSBitmapImageRep * rep = glue_NSImage_to_NSBitmapImageRep( ns );

   return [[[CIImage alloc]
            initWithBitmapImageRep: rep] autorelease];
}

extern NSImage * glue_CIImage_to_NSImage( CIImage * ci )
{
   int width   = [ci extent].size.width;
   int height  = [ci extent].size.height;

   NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(width,height)] autorelease];
   [img lockFocus];
   [[[NSGraphicsContext currentContext] CIContext]
    drawImage: ci 
    atPoint: CGPointMake( 0,0 )
    fromRect: [ci extent]];
   [img unlockFocus];
   return img;
}

The Racket FFI definitions for the above functions:

(provide (all-defined-out))

(require ffi/unsafe)
(require ffi/unsafe/define)
 
(define glue-lib (ffi-lib "./cocoa-glue/build/Debug/cocoa-glue"))

(define-syntax define-glue
  (syntax-rules (:)
    [(_ name : type ...)
     (define name
       (get-ffi-obj
        (regexp-replaces 'name '((#rx"-" "_")))
        glue-lib (_fun type ...)))]))

(define-glue glue-image-width : _pointer -> _int32)
(define-glue glue-image-height : _pointer -> _int32)
(define-glue glue-CIImage-to-NSImage : _pointer -> _pointer)
(define-glue glue-NSImage-to-CIImage : _pointer -> _pointer)
(define-glue glue-NSBitmapImageRep-to-texture : _pointer -> _uint32)
(define-glue glue-NSImage-to-NSBitmapImageRep : _pointer -> _pointer)

Leave a Reply

Your email address will not be published. Required fields are marked *