Alright, it's time for the next step. I have successfully managed to implement a Bitmap that loads a file properly. I have also made the part in Sprite work that lets it draw the Bitmap. Here comes the tricky part and you have to be careful when you code. I will explain everything.
1. Let me first explain what gc_mark and gc_free functions will be used for. The GC (garbage collector) works like this: It has a mark-phase and a collect-phase. During the mark phase it marks all objects accessible from Ruby. We have to mark C++ objects manually so the GC doesn't destroy them. Basically when an object is not marked during the mark-phase, the GC assumes that all references are lost and that it's time to delete that object. The collect-phase then simply deletes all inaccessible objects. At that moment the gc_free function is called to clean up additional stuff if necessary.
2. This is what we will use the ClassName::gc_mark function for; we will mark C++ objects that are referenced by this object. Here is a simple example:
// Sprite::rb_bitmap is of type VALUE! The reason is explained later.
void Sprite::gc_mark(Sprite* sprite)
{
if (!NIL_P(sprite->rb_bitmap))
{
rb_gc_mark(sprite->rb_bitmap);
}
}
This makes sure that the bitmap is no immediately destroyed when Ruby loses all references to that object.
3. gm_free won't be necessary in most cases. A rare case is the Bitmap class that allocates an external object. It allocates an april::Texture instance and has to get rid of it once the bitmap itself has been destroyed.
4. One of the first things I noticed is that there are problems during the GC's mark phase if you create and kind of new variable. Even though using Data_Wrap_Struct does not actually create a new object but only wraps a C++ object into a Ruby object, it seems that on the Ruby side it is considered a new object. Basically gc_mark functions crash the GC (and therefore the whole game) if Data_Wrap_Struct is called during the gc_mark call. This is one thing to keep in mind.
5. I have figured out a simple workaround for the previous problem, but it will require us to be extra careful with the code. Basically we will have to have a Ruby object reference to C++ objects in our objects. The Sprite class with the Bitmap class is a good example for that.
class Sprite
{
Bitmap* bitmap; // C++ Bitmap object
VALUE rb_bitmap; // Ruby Bitmap object
}
As you noticed under 2., I have been using the rb_bitmap when I was marking it for the GC. Sadly we will have to do this. I remember that RMXP was able to crash if you call GC.start at some points. That made me think a bit. I think that Enterbrain didn't do that part actually right. It even makes quite a bit of sense. I remember when I tried to implement a cache emptying functionality in CP and it would crash with an unusual error message even though all Ruby references to the bitmaps have been removed.
I'm going to post a new task after I have finalized the last few things and committed everything.
EDIT: I have also added section separations to the Color and Sprite class' source code. When you work on another class, feel free to do the same.