Compiling ZaaIL with Alchemy – Part 3

So now you’ve compiled DevIL! Congratulations! However, you may notice there’s still no .swc or .swf for you to use. We still have one more step to complete.

Currently we have all the libraries compiled with Alchemy, but we haven’t defined the interface that we’ll expose to Flash yet. There are two methods to do this:

1) Write C code directly that defines this relationship.
2) Write GlueGen (gg) to define this relationship, and then use another one of Alchemy’s tools to turn this glue code into the corresponding C and AS code.

For ZaaIL, we used option 2, creating a GlueGen interface.

I created a file named devil.gg and put this file in the DevIL library directory. If you decide to create it elsewhere the path to il.h will need to be adjusted accordingly. The documentation on GlueGen syntax and features is pretty sparse (Quick heads up… there is a similar tool for Java), so some of this may not be the best way, but I found that it worked.

The documentation we do have is available here.

At the top of this file you define a code block that will be passed straight through to the C file.

// some C code with some includes -- top level blocks get emitted as-is to the C glue
{
    #include
    #include "include/IL/il.h"
}

We then import ByteArray and define functions that you want to make available to Flash. The first function, ilInit, is about as simple as it gets. Flash is going to be able to call ilInit which will call the C ilInit, no arguments are involved and no return type to worry about.

The next function ilLoadImage has parameters and a return type we need to handle. I’m not sure about the naming of the incoming parameters to these functions, so I just named them the same as the expected values on the C side. Then with the return type we are saying that C will return ILboolean, but on the AS side we want a uint (this is called marshalling).

// import the ByteArray class -- gets emitted into AS glue
import flash.utils.ByteArray;
 
public function ilInit():void;
 
public function ilLoadImage(path:String):(ILboolean)uint;
 
public function ilGetError():(ILenum)uint;
 
public function ilGetInteger(Mode:uint):(unsigned)uint;
 
public function ilOriginFunc(Mode:uint):(ILboolean)uint;
 
public function ilEnable(Mode:uint):(ILboolean)uint;

Lastly, we can implement functions of our own that we can call from AS. Below I define a function named ilGetPixels, which wraps the C function ilCopyPixels. This function has a body and actually does some work to ensure that we create a byte array that is in the format Actionscript wants (which is ARGB as opposed to RGBA which DevIL provides).

public function ilGetPixels(offset_x:int, offset_y:int, offset_z:int, width:int, height:int, depth:int, data:ByteArray):void
{
 
	int bytes_read;
	int full_width;
	int full_height;
	int full_depth;
	int bpp;
	int bpc;
	int i;
	char r, g, b, a;
	unsigned int byte;
	ILubyte * bytes;
 
	// Get the values from the library
	bpp = ilGetInteger(IL_IMAGE_BPP);
	bpc = ilGetInteger(IL_IMAGE_BPC);
	full_width = ilGetInteger(IL_IMAGE_WIDTH);
	full_height = ilGetInteger(IL_IMAGE_HEIGHT);
	full_depth = ilGetInteger(IL_IMAGE_DEPTH);
 
	// Check the given values
	if (offset_x + width > full_width)
		width = (full_width - offset_x);
	if (offset_y + height > full_height)
		height = (full_height - offset_y);
	if (offset_z + depth > full_depth)
		depth = (full_depth - offset_z);
 
	// Now get the information from the library
	bytes_read = width * height * depth * 4 * bpc;
	bytes = (ILubyte*)malloc(bytes_read);
	ilCopyPixels(offset_x, offset_y, offset_z, width, height, depth, IL_RGBA, IL_UNSIGNED_BYTE, bytes);
 
	// Switch RGBA to ARGB
	for (i=0; i < bytes_read; i+=4)
	{
		r = bytes[i];
		g = bytes[i+1];
		b = bytes[i+2];
		a = bytes[i+3];
 
		bytes[i] = a;
		bytes[i+1] = r;
		bytes[i+2] = g;
		bytes[i+3] = b;
	}
 
	// Copy the pixels into the bytes array
	AS3_SetS(data, "position", NULL);
	AS3_ByteArray_writeBytes(data, bytes, bytes_read);
 
	// Free up the memory
	free(bytes);
 
}

So the file in its entirety looks like this

// some C code with some includes -- top level blocks get emitted as-is to the C glue
{
	#include
	#include "include/IL/il.h"
}
 
// "int" AS3 <=> C "int"
// "uint" AS3 <=> C "unsigned"
// "Number" AS3 <=> C "double"
// "Boolean" AS3 <=> C "int"
// "String" AS3 <=> C malloc-ed "const char *" (returning one will free it!)
// else AS3 <=> C "AS3_Val"
 
// import the ByteArray class -- gets emitted into AS glue
import flash.utils.ByteArray;
 
public function ilInit():void;
 
public function ilLoadImage/ilLoadImage(path:String):(ILboolean)uint;
 
public function ilGetError():(ILenum)uint;
 
public function ilGetInteger(Mode:uint):(unsigned)uint;
 
public function ilOriginFunc(Mode:uint):(ILboolean)uint;
 
public function ilEnable(Mode:uint):(ILboolean)uint;
 
public function ilGetPixels(offset_x:int, offset_y:int, offset_z:int, width:int, height:int, depth:int, data:ByteArray):void
{
 
	int bytes_read;
	int full_width;
	int full_height;
	int full_depth;
	int bpp;
	int bpc;
	int i;
	char r, g, b, a;
	unsigned int byte;
	ILubyte * bytes;
 
	// Get the values from the library
	bpp = ilGetInteger(IL_IMAGE_BPP);
	bpc = ilGetInteger(IL_IMAGE_BPC);
	full_width = ilGetInteger(IL_IMAGE_WIDTH);
	full_height = ilGetInteger(IL_IMAGE_HEIGHT);
	full_depth = ilGetInteger(IL_IMAGE_DEPTH);
 
	// Check the given values
	if (offset_x + width > full_width)
		width = (full_width - offset_x);
	if (offset_y + height > full_height)
		height = (full_height - offset_y);
	if (offset_z + depth > full_depth)
		depth = (full_depth - offset_z);
 
	// Now get the information from the library
	bytes_read = width * height * depth * 4 * bpc;
	bytes = (ILubyte*)malloc(bytes_read);
	ilCopyPixels(offset_x, offset_y, offset_z, width, height, depth, IL_RGBA, IL_UNSIGNED_BYTE, bytes);
 
	// Switch RGBA to ARGB
	for (i=0; i < bytes_read; i+=4)
	{
		r = bytes[i];
		g = bytes[i+1];
		b = bytes[i+2];
		a = bytes[i+3];
 
		bytes[i] = a;
		bytes[i+1] = r;
		bytes[i+2] = g;
		bytes[i+3] = b;
	}
 
	// Copy the pixels into the bytes array
	AS3_SetS(data, "position", NULL);
	AS3_ByteArray_writeBytes(data, bytes, bytes_read);
 
	// Free up the memory
	free(bytes);
 
}

Now that we have our GlueGen file created, we’re ready to convert the GlueGen into C and AS. This can be accomplished by running

GlueGen devil.gg -cpackage cmodule.devil -package Zaa.IL -class ZaaIL

This will create glue.c and glue.as. With these files we can create our swc! We now need to turn Alchemy on again, if you’ve turned it off, by running alc-on. We can then use gcc to compile our glue.c file. When we do run this command, we once again need to tell gcc where to find the libraries that its linking against, since they’re static libraries. My command looked like this:

alc-on
sudo gcc glue.c -O3 -Llib/.libs -lIL -L../jpeg-8a -ljpeg -L../libpng-1.2.43 -lpng -L../zlib -lz -L../libmng-1.0.10 -lmng -swc -o zaail.swc

If you look in this directory, you should now see zaail.swc! There it is in all its glory! You now have a fully functional swc, copy it into the lib directory of your project and you’re off!

Now we need to discuss how exactly you can use your shiny new swc. There is some boiler plate code you need to get working with your object.

import cmodule.zaail.CLibInit;
private var loader:CLibInit = new CLibInit();
private var lib:Object = loader.init();

And that’s about it for the boiler plate. Import what you need and setup a couple of objects. Then you’re ready to call functions on your object. Here is the setup code for ZaaIL:

lib.ilInit();
lib.ilOriginFunc(ZaaILInterface.IL_ORIGIN_UPPER_LEFT);
lib.ilEnable(ZaaILInterface.IL_ORIGIN_SET);

You’ll notice that these are functions that we had exposed through the GlueGen. More about how you can interact with these libraries can be found here. Since this is actually running a virtual machine within the Flash Player some interactions with the C code have to be done differently. For instance to allow the virtual machine to access a file, you have to call supplyFile, give it the path and the ByteArray for the contents of the file.

Let us know if you have questions in the comments below.

Tags: , ,

4 Responses to “Compiling ZaaIL with Alchemy – Part 3”

  1. cb May 24, 2010 at 10:27 pm #

    hi.

    i patterned your instruction on building a library A with dependency library B. however I got runtime Undefined sym: _method error.

    Compiling library A&B uses libtool, I configured B with “./configure –enable-shared=false –enable-static=true” to make it static.

    But as I said, I still got a Undefined Sym error. Have I missed something? Or do I need to tweak Makefile of B not to use libtool at all. thanks.

  2. Logan October 12, 2011 at 8:30 am #

    I have the same problem with @cb.

    I think the linker does not work correctly.

Trackbacks/Pingbacks

  1. Compiling ZaaIL with Alchemy – Part 2 | ZaaLabs - May 17, 2010

    […] Compiling ZaaIL with Alchemy – Part 3 […]

  2. Compiling ZaaIL with Alchemy – Part 1 | ZaaLabs - May 17, 2010

    […] Compiling ZaaIL with Alchemy – Part 3 […]

Leave a Reply