To illustrate the use of C API's with Perl objects, a toy PPM-format
image class is created and then extended (ToyPPM.pm and ExtendedToyPPM.pm).
This child class runs just as fast as if it were part of a big
C-language extension library, instead of being small and independent,
depending only on ToyPPM.pm having provided a C API, and on Inline::C,
to make use of it.
The image data is maintained in the PPM string itself, as sequence of (height x width x 3) one-byte integer color samples. These samples are exposed as a seemingly normal perl array, implemented by a Tie::Array/perltie-based helper subclass, created from a simple specification, by a generator of array-like classes. This sample array is then "folded" into an apparently normal 3D array of array of arrays, to provide a very natural interface to the image data (though a quite slow one). ToyPPM.pm also provides a C API, namely a method which returns a string of C code. It defines macros which obtain a pointer to the string's memory, and provide direct and rapid access to the image bytes. This method was created by the same generator, also from a simple specification. ExtendedToyPPM.pm takes this string of C code, which provides macros for accessing the image data, and combines it with a reference implementation of a line walker, to create the C code for a line drawer. This is then handed to Inline::C, creating a method draw_line(). Which runs just as fast as if it had been hand coded as part of an image library. Without having to sacrifice object-oriented-ness. |
Up
|
This
$image = ToyPPM->new_from_ppm_data("P6 2 2 255 RgbRgbRgbRgb");creates a 2 by 2 pixel RGB image (a greenish one). The string itself is used to store the pixels.
A ToyPPM object looks both like a string,
and like a normal
perl 3D array (ie, an array of arrays of arrays).
SYNOPSIS use ToyPPM; $image = ToyPPM->new_from_ppm_data(`cat some.ppm`); $image = ToyPPM->new_from_ppm_data("P6 2 2 255 RgbRgbRgbRgb"); print OUT $image; @rows = @{$image}; # The rest is just normal array manipulation... @rgb_of_pixel_0_0 = @{$image->[0][0]}; @{$image->[0][0]} = (255,0,0); # red $lower_right_blue_value = $image->[1][1][2]; $image->[1][1][2] = 255; $pixel01 = $image->[0][1]; $pixel01->[1] = $new_green; $width = @{$image->[0]}; # scalar contextSimple.
It also defines a
$c_code = $image->get_toy_api_ImageCMacros();which returns a C api
#define DECL(image_object) ... #define INIT(image_object) ... #define HEIGHT() ... #define GET_R(x,y) ... #define SET_R(x,y,r) ... #define SET_RGB(x,y, r,g,b) ... ...etc...
With this, ExtendedToyPPM.pm
extends ToyPPM with a draw_line() method.
Which is written in C, and
is very, very fast.
Note that
Punchline: At the cost of dealing with C API's and Inline::C, one can get the performance of a big monolithic C extension, while using only normal object-oriented practice. Objects which are nicely small, orthogonal, mixable, inheritable, and independent. With all the project- and community- level benefits which follow.
Comments encouraged - Mitchell Charity <[email protected]>
The ToyDrawLine api is a C macro. There are more flexible options, but they cost an extra line or three in ExtendedToyPPM. So this was mostly a pedagogical choice. I'm not sure it was the right one.
This toy draw_line() will explode if given off-image coordinates. Normally this is handled by the generated array class, but I yanked it to shorten ToyDefineArrayMethods. I could add it to draw_line(), but it doesn't belong there. So... that is what happened to safety.
This ToyArrayFold utility class was just a quick hack. So this "$image->[0][0] = [255,0,0];" doesn't work. Nor do push(), pop(), etc. If there is interest, I can dust off a more complete one.
Regards the PPM format, here is a manpage, another, an overview, and the minimalist netpbm homepage (netpbm replaced the old pbmplus). The toy doesn't handle two-byte samples, nor all the places comments can occur.
This toy code was created solely for this illustration. It was derived, with substantial changes, from a pre-alpha code base. It has not been tested. It follows that it must have a surfeit of bugs, in addition to the many misfeatures knowingly introduced. Just so you know. Please let me know if there is interest.
Notes:
Perl source formating was provided by Perltidy. C formatting by enscript.
Doables:
History:
2002-Apr-09 Online.