I have been learning a little about CAPI, the GUI library that comes with LispWorks. I particularly wanted to find out how to draw graphics primitives inside a window, as I wanted to modify Paul Graham's toy ray-tracer to display images on screen.
CAPI offers essentially the same 2D graphics programming model as the Java toolkits I'm more familiar with. From section 11 of the user manual, it appears that CAPI elements relate to some basic Java AWT concepts as follows:
- The CAPI:OUTPUT-PANE class defines a canvas for drawing.
- Graphics port objects act as graphics contexts.
- An OUTPUT-PANE instance's DISPLAY-CALLBACK slot can be given a function to handle paint requests. Usually, this callback function will invoke functions in the GRAPHICS-PORTS package, such as DRAW-POINT, in order to draw into the pane's graphics port.
On to the task at hand. In his book for learners, ANSI Common Lisp, Paul Graham develops a bare bones ray-tracer to illustrate number crunching in Lisp (code available at the book's web site). Graham designed the tracer to output images as PGM format files. My aim here is to render the image onto an OUTPUT-PANE, and do so by making minimal changes to PG's original code.
The original TRACER function computed the greyscale value of each pixel in the image and wrote the results into a file. The version below computes the same results, but puts them into an *IMAGE* array intended for use by a display callback function.
(DEFVAR *IMAGE* NIL
"2D array containing the pixel-map of the rendered image.
(AREF *IMAGE* Y X) gives colour at point (X,Y), as an index
into *GREYS*.")
(DEFVAR *GREYS*
(COERCE (LOOP FOR LEVEL BELOW 256
COLLECT (COLOR:MAKE-GRAY (/ LEVEL 255s0)))
'VECTOR)
"Vector of 256 shades of grey, from black to white.")
(DEFUN TRACER (PATHNAME &OPTIONAL (RES 1))
(DECLARE (IGNORE PATHNAME))
(SETF *IMAGE* (MAKE-ARRAY (LIST (* RES 100) (* RES 100))))
(LOOP WITH INC = (/ RES)
FOR Y FROM -50 BELOW 50 BY INC
FOR ROW FROM 0
DO (LOOP FOR X FROM -50 BELOW 50 BY INC
FOR COL FROM 0
DO (SETF (AREF *IMAGE* ROW COL)
(COLOR-AT X Y))))
(CAPI:CONTAIN (MAKE-INSTANCE 'CAPI:OUTPUT-PANE
:DISPLAY-CALLBACK 'RENDER)
:BEST-WIDTH (* 100 RES)
:BEST-HEIGHT (* 100 RES)))
TRACER ends by creating a window using CAPI:CONTAIN and furthermore registers RENDER as the window's display function.
(DEFUN RENDER (PORT X Y WIDTH HEIGHT)
(LOOP WITH (BOTTOM RIGHT) =
(MAPCAR #'1- (ARRAY-DIMENSIONS *IMAGE*))
FOR ROW FROM Y TO (MIN BOTTOM (+ Y HEIGHT))
DO (LOOP FOR COL FROM X TO (MIN RIGHT (+ X WIDTH))
DO (GRAPHICS-PORTS:DRAW-POINT
PORT COL ROW
:FOREGROUND
(SVREF *GREYS*
(AREF *IMAGE* ROW COL))))))
And that's all there is to it.

