What I did in the previous post is fine if we want to write a file right to disk, but what if we want to get the image data back from ImageMagick?
To do this we will need to use the #_MagickGetImageBlob call.
From the ImageMagick Documentation:
MagickGetImageBlob MagickGetImageBlob() implements direct to memory image formats. It returns the image as a blob and its length. Use MagickSetFormat() to set the format of the returned blob (GIF, JPEG, PNG, etc.). Use MagickRelinquishMemory() to free the blob when you are done with it. The format of the MagickGetImageBlob method is: unsigned char *MagickGetImageBlob(MagickWand *wand,size_t *length) A description of each parameter follows: wand the magick wand. length the length of the blob.
We have already made plenty of calls where we use a pointer to MagickWand, but we have not yet done anything where we need to allocate memory to pass to our foreign function.
Luckily this isn’t too hard and this is a pretty trivial example.
First we set up the environment. I’m assuming we’ve done the stuff that I described in my first part of the tutorial, that is, set up the interface directory.
CL-USER> (ccl:use-interface-dir :imagemagick) # CL-USER> (ccl:use-interface-dir :libc) # ; we load the libc interface so that we know what size size_t is CL-USER> (ccl:open-shared-library "libMagickWand.so") # CL-USER> (setf wand (#_NewMagickWand)) #<A Foreign Pointer#x1CB83F0 > CL-USER> (#_MagickSetSize wand 100 100) 1 CL-USER> (with-cstrs ((image-format "png")) (#_MagickSetFormat wand image-format)) ;Compiler warnings : ; In an anonymous lambda form: Undeclared free variable WAND 1 CL-USER> (with-cstrs ((red-image "xc:red")) (#_MagickReadImage wand red-image)) ;Compiler warnings : ; In an anonymous lambda form: Undeclared free variable WAND 1
Now we have a wand that is a png, 100×100 and solid red. What we want to do next is allocate our memory, call our blob function, convert the return value to a string and free all of the memory.
To create the space for the length, we use the make-record macro (found in the FFI Documentation). Once we do this, we can call the Blob function.
The make-record macro retuns a pointer, and to be able to see what is in the memory at the pointer, we can use the ccl:pref function, and to free the pointer that we created, we use the ccl:free function. To translate c data to a string, we can use either ccl:%get-cstring if we know it is a normal C string (nul terminated) or, if we know the length, we can use ccl:%str-from-ptr (documented here
CL-USER> (setf length (ccl:make-record #>size_t)) #<A Foreign Pointer (:* (:UNSIGNED 64)) #x1D21080> (ccl:pref length #>size_t) 0 CL-USER> (setf imagedata (#_MagickGetImageBlob wand length)) #<A Foreign Pointer #x7FCE070F1010 > CL-USER> (ccl:pref length #>size_t) 477 CL-USER> (setf imagestr (ccl:%str-from-ptr imagedata (ccl:pref length #>size_t))) "\211PNG^M . . . bunch of stuff that I can't paste here" CL-USER> (ccl:free length) NIL CL-USER> (#_MagickRelinquishMemory imagedata) #<A Null Foreign Pointer> CL-USER> (#_DestroyMagickWand wand) #<A Null Foreign Pointer>
So, now we have gotten our string, we can push that out using hunchetoot (or whatever, just make sure you set the content-type to “image/png”), and we have cleaned up after ourselves.