2009年4月2日星期四

Move Ruby/GD To Ruby 1.9.1

If you are looking for Ruby extension to use GD library, plese check here.

因为实在无法忍受JRuby调用JAVA API处理Jpeg的龟速,所以花了点时间改了一下Ruby/GD的代码,使其能运行在mswin32版Ruby 1.9.1下。成果是有一点,不过还是有暂时无法解决的问题。

Ruby/GD用到的API主要是IO部分,从Ruby的头文件定义来看,变动不小。以下这几个宏,是我从RMagick等其他扩展里抄来的:

// GetReadFile doesn't exist in Ruby 1.9.0
#if !defined(GetReadFile)
#define GetReadFile(fptr) rb_io_stdio_file(fptr)
#define GetWriteFile(fptr) rb_io_stdio_file(fptr)
#endif

// rb_io_t replaces OpenFile in Ruby 1.9.0
#if defined(HAVE_RB_IO_T)
#undef OpenFile
#define OpenFile rb_io_t
#endif

// These macros are required in 1.9.1 but aren't defined prior to 1.8.6.
#if !defined(RSTRING_LEN)
#define RSTRING_LEN(s) (RSTRING((s))->len)
#endif
#if !defined(RSTRING_PTR)
#define RSTRING_PTR(s) (RSTRING((s))->ptr)
#endif

// Backport these two macros to 1.8
#if !defined(RARRAY_LEN)
#define RARRAY_LEN(a) RARRAY((a))->len
#endif
// Matz says this macro is read-only! (see http://www.ruby-forum.com/topic/146072)
#if !defined(RARRAY_PTR)
#define RARRAY_PTR(a) RARRAY((a))->ptr
#endif

变化很大,而且还没解决的问题在rb_io_t结构体上。Ruby 1.8中,该结构体包括两个*File指针,虽然不清楚区别是什么,反正是可写的。而Ruby 1.9中rb_io_t增加了很多内容,*File指针变为一个,而且不可写。

static VALUE
img_from_jpeg(VALUE img, VALUE out, VALUE quality)
{
    gdImagePtr im;
    OpenFile *fptr;
    FILE *f;

    Data_Get_Struct(img, gdImage, im);
    Check_Type(out, T_FILE);
    rb_io_binmode(out);
    GetOpenFile(out, fptr);
    rb_io_check_writable(fptr);
    f = GetWriteFile(fptr);
    gdImageJpeg(im, f, FIX2INT(quality));
    return img;
}

以上函数就这么看似乎没什么不对,可就是会出错。

另外,我还发现Ruby/GD的API设计也怪怪的,有那么点不合理。比如载入Jpeg文件,Ruby/GD有两个方法:GD::Image.newFromJpeg和GD::Image.new_from_jpeg。这两个并非同名函数,前者接受的参数是File对象,后者接受文件路径。再比如copy方法,其定义为:srcimg.copy(destimg, dx, dy, sx, sy, w, h)。调用该方法后,有变化的是作为参数的destimg,而非调用者的srcimg,这似乎与平常的习惯相反。

改到最后,虽然只有newFromXXX系列函数不能用,可怎么着都觉得有点不舒服。最后还是以Ruby/GD为蓝本,改写了一个扩展出来,有兴趣的话参见这里。至于我改的Ruby/GD就不提供下载了,实在需要的话,留言吧。

没有评论 :