From: Ralph B. <ra...@de...> - 2005-07-14 20:08:03
|
Hey everyone, I'm having a bit of trouble using NSBitmapImageRep#bitmapData. Or at least, I think I am. (Source bits are included below, there is a testcase up at http://deadfeed.net/sandbox/testcase.zip - beware, the Makefile dependencies are not 100%.) The NSBitmapImageRep should display data in a window, and that data should be coming from a C++ extension, via SWIG. Ruby should create the window, create views, and call a C++ function to fill the bitmap. But I all I get is a bus error (presumably because the pointer handed to the C++ function is off). I have a class, FracWindow, which initializes the window and views (@fracView). FracWindow#ShowImage passes a pointer (char *) to the C++ function, which then fills it. def showImage() bitmapRep=OSX::NSBitmapImageRep.alloc.initWithBitmapDataPlanes(nil, :pixelsWide, 256, :pixelsHigh, 256, :bitsPerSample, 8, :samplesPerPixel, 4, :hasAlpha, true, :isPlanar, true, :colorSpaceName, "NSDeviceRGBColorSpace", :bytesPerRow, 0, :bitsPerPixel, 0 ) Testinterface::Test(bitmapRep.bitmapData()) image=OSX::NSImage.alloc.initWithSize([256, 256]) image.addRepresentation(bitmapRep) @fracView.setImage(image) @fracView.setNeedsDisplay(true) end Testinterface::Test looks like this: void Test(BitmapPointer p) { std::cout << &p << std::endl; unsigned char *pp=p; pp[0]=255; pp[1]=0; pp[2]=0; pp[3]=0; pp[4]=0; pp[5]=0; pp[6]=0; pp[7]=0; /* for (int y=0; y<256; y++) { for (int x=0; x<256; x++) { char f=y/2+x/2; *pp++=f; // *pp++=f; // *pp++=f; // *pp++=f; } }*/ } I made a BitmapPointer typedef in order to let SWIG distinguish the data type (char *). The SWIG typemap looks like this: %typemap(in) BitmapPointer { if (TYPE($input)==T_STRING) { $1 = StringValuePtr($input); } else { rb_raise(rb_eTypeError, "Expected bitmapdata"); $1 = 0; } } The main event here is of course "$1 = StringValuePtr($input);", which should, as I understand it return a char pointer to the string data which bitmapRep.bitmapData() returned. The reason it is converted to a Ruby string, I assume, is because RubyCocoa sees the char *, and thinks it is a string. Perhaps the string is copied to an internal buffer? I'd appreciate it if anyone could point me in the right direction. Regards, Ralph Brorsen |
From: Jonathan P. <jp...@dc...> - 2005-07-14 22:53:38
|
On 14 Jul 2005, at 21:08, Ralph Brorsen wrote: > The main event here is of course "$1 = StringValuePtr($input);", > which should, as I understand it return a char pointer to the > string data which bitmapRep.bitmapData() returned. The reason it is > converted to a Ruby string, I assume, is because RubyCocoa sees the > char *, and thinks it is a string. Perhaps the string is copied to > an internal buffer? As you've identified, the return value of bitmapData is being converted to a ruby string. The easiest solution (since you're already dealing with some native code) may be to pass the NSBitmapImageRep pointer to your native code and get the pointer to the bitmapData from within your native code. A better solution would involve some changes to RubyCocoa to better support this. You could do the 'easy' solution from Objective C++ or link in a small bit of Objective C code to do the work for you. The Objective C object pointer (of type 'id' or whatever your object is - e.g., 'NSBitmapImageRep*') is available by calling __ocid__ on the ruby object. So, from within your native code you could do something like: id ocobj = (id) NUM2UINT( rb_funcall(obj, rb_intern("__ocid__"), 0) ); unsigned char *pixels = [(NSBitmapImageRep*) ocobj bitmapFormat]; Another option would be to call into RubyCocoa to get it to do this for you. There's a function 'rbobj_get_ocid' which does what you want (in a more robust fashion). The C function prototype (from mdl_osxobjc.h) is: id rbobj_get_ocid (VALUE rcv); Provided you can get the linker to be happy with this (e.g., by linking your native code against RubyCocoa too) it should work. I'm sorry if the above is a bit vague. I've not tested it (just typed into email), but something along those lines should work. Jonathan |
From: Ralph B. <ra...@de...> - 2005-07-16 10:45:03
|
Thanks Jonathan! I was able to get it to work. If at all possible I would prefer not to link my C++ stuff against Ruby in order to keep it clean. It is a library meant for stand-alone distribution. I now handle the conversion in the SWIG layer: // Convert bool from Ruby --> C with type check %typemap(in) BitmapPointer { id ocobj = (id) NUM2UINT( rb_funcall($input, rb_intern("__ocid__"), 0) ); $1 = [(NSBitmapImageRep*) ocobj bitmapData]; } Three more things were needed to make it work. 1. Testinterface::Test(bitmapRep.bitmapData()) -> Testinterface::Test(bitmapRep) (duh) 2. #include <AppKit/NSBitmapImageRep.h> (in the SWIG .i file) 3. You're now mixing Objective-C and C/C++ (aptly termed Objective-C++), and the compiler needs to be made aware of this. This is done by changing the extension of the *_wrap.cxx file to .mm, ie. *_wrap.mm. Update your makefile. Thanks again, Jonathan. Ralph On Jul 15, 2005, at 00:52, Jonathan Paisley wrote: > On 14 Jul 2005, at 21:08, Ralph Brorsen wrote: > >> The main event here is of course "$1 = StringValuePtr($input);", >> which should, as I understand it return a char pointer to the string >> data which bitmapRep.bitmapData() returned. The reason it is >> converted to a Ruby string, I assume, is because RubyCocoa sees the >> char *, and thinks it is a string. Perhaps the string is copied to an >> internal buffer? > > As you've identified, the return value of bitmapData is being > converted to a ruby string. The easiest solution (since you're already > dealing with some native code) may be to pass the NSBitmapImageRep > pointer to your native code and get the pointer to the bitmapData from > within your native code. |
From: Jonathan P. <jp...@dc...> - 2005-07-16 13:28:15
|
On 15 Jul 2005, at 21:24, Ralph Brorsen wrote: > If at all possible I would prefer not to link my C++ stuff against > Ruby in order to keep it clean. It is a library meant for stand- > alone distribution. I now handle the conversion in the SWIG layer: > > // Convert bool from Ruby --> C with type check > %typemap(in) BitmapPointer { > id ocobj = (id) NUM2UINT( rb_funcall($input, rb_intern > ("__ocid__"), 0) ); > $1 = [(NSBitmapImageRep*) ocobj bitmapData]; > } Did you try linking against RubyCocoa to make use of its dedicated function for getting at the __ocid__ of a Ruby object? That way will rely less on the internal workings of RubyCocoa. > Three more things were needed to make it work. > 1. Testinterface::Test(bitmapRep.bitmapData()) -> > Testinterface::Test(bitmapRep) (duh) > 2. #include <AppKit/NSBitmapImageRep.h> (in the SWIG .i file) > 3. You're now mixing Objective-C and C/C++ (aptly termed Objective-C > ++), and the compiler needs to be made aware of this. This is done > by changing the extension of the *_wrap.cxx file to .mm, ie. > *_wrap.mm. Update your makefile. Thanks for posting back with your results. Useful recipes. Jonathan |